Merge from trunk.
[awesomized/libmemcached] / clients / memcapable.c
1 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 #undef NDEBUG
3 #include "config.h"
4 #include <pthread.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <netdb.h>
8 #include <arpa/inet.h>
9 #include <netinet/in.h>
10 #include <netinet/tcp.h>
11 #include <fcntl.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <errno.h>
16 #include <assert.h>
17 #include <string.h>
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <unistd.h>
21 #include <poll.h>
22 #include <ctype.h>
23
24 #include <libmemcached/memcached/protocol_binary.h>
25 #include <libmemcached/byteorder.h>
26
27 #ifdef linux
28 /* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to
29 * optimize the conversion functions, but the prototypes generate warnings
30 * from gcc. The conversion methods isn't the bottleneck for my app, so
31 * just remove the warnings by undef'ing the optimization ..
32 */
33 #undef ntohs
34 #undef ntohl
35 #endif
36
37 /* Should we generate coredumps when we enounter an error (-c) */
38 static bool do_core= false;
39 /* connection to the server */
40 static int sock;
41 /* Should the output from test failures be verbose or quiet? */
42 static bool verbose= false;
43
44 /* The number of seconds to wait for an IO-operation */
45 static int timeout= 2;
46
47 /*
48 * Instead of having to cast between the different datatypes we create
49 * a union of all of the different types of pacages we want to send.
50 * A lot of the different commands use the same packet layout, so I'll
51 * just define the different types I need. The typedefs only contain
52 * the header of the message, so we need some space for keys and body
53 * To avoid to have to do multiple writes, lets add a chunk of memory
54 * to use. 1k should be more than enough for header, key and body.
55 */
56 typedef union
57 {
58 protocol_binary_request_no_extras plain;
59 protocol_binary_request_flush flush;
60 protocol_binary_request_incr incr;
61 protocol_binary_request_set set;
62 char bytes[1024];
63 } command;
64
65 typedef union
66 {
67 protocol_binary_response_no_extras plain;
68 protocol_binary_response_incr incr;
69 protocol_binary_response_decr decr;
70 char bytes[1024];
71 } response;
72
73 enum test_return
74 {
75 TEST_SKIP, TEST_PASS, TEST_PASS_RECONNECT, TEST_FAIL
76 };
77
78 /**
79 * Try to get an addrinfo struct for a given port on a given host
80 */
81 static struct addrinfo *lookuphost(const char *hostname, const char *port)
82 {
83 struct addrinfo *ai= 0;
84 struct addrinfo hints= {.ai_family=AF_UNSPEC,
85 .ai_protocol=IPPROTO_TCP,
86 .ai_socktype=SOCK_STREAM};
87 int error= getaddrinfo(hostname, port, &hints, &ai);
88
89 if (error != 0)
90 {
91 if (error != EAI_SYSTEM)
92 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
93 else
94 perror("getaddrinfo()");
95 }
96
97 return ai;
98 }
99
100 /**
101 * Set the socket in nonblocking mode
102 * @return -1 if failure, the socket otherwise
103 */
104 static int set_noblock(void)
105 {
106 int flags= fcntl(sock, F_GETFL, 0);
107 if (flags == -1)
108 {
109 perror("Failed to get socket flags");
110 close(sock);
111 return -1;
112 }
113
114 if ((flags & O_NONBLOCK) != O_NONBLOCK)
115 {
116 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
117 {
118 perror("Failed to set socket to nonblocking mode");
119 close(sock);
120 return -1;
121 }
122 }
123
124 return sock;
125 }
126
127 /**
128 * Try to open a connection to the server
129 * @param hostname the name of the server to connect to
130 * @param port the port number (or service) to connect to
131 * @return positive integer if success, -1 otherwise
132 */
133 static int connect_server(const char *hostname, const char *port)
134 {
135 struct addrinfo *ai= lookuphost(hostname, port);
136 sock= -1;
137 if (ai != NULL)
138 {
139 if ((sock=socket(ai->ai_family, ai->ai_socktype,
140 ai->ai_protocol)) != -1)
141 {
142 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1)
143 {
144 fprintf(stderr, "Failed to connect socket: %s\n",
145 strerror(errno));
146 close(sock);
147 sock= -1;
148 }
149 else
150 {
151 sock= set_noblock();
152 }
153 } else
154 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
155
156 freeaddrinfo(ai);
157 }
158
159 return sock;
160 }
161
162 static ssize_t timeout_io_op(int fd, short direction, void *buf, size_t len)
163 {
164 ssize_t ret;
165
166 if (direction == POLLOUT)
167 ret= write(fd, buf, len);
168 else
169 ret= read(fd, buf, len);
170
171 if (ret == -1 && errno == EWOULDBLOCK) {
172 struct pollfd fds= {
173 .events= direction,
174 .fd= fd
175 };
176 int err= poll(&fds, 1, timeout * 1000);
177
178 if (err == 1)
179 {
180 if (direction == POLLOUT)
181 ret= write(fd, buf, len);
182 else
183 ret= read(fd, buf, len);
184 }
185 else if (err == 0)
186 {
187 errno= ETIMEDOUT;
188 }
189 else
190 {
191 perror("Failed to poll");
192 return -1;
193 }
194 }
195
196 return ret;
197 }
198
199 /**
200 * Ensure that an expression is true. If it isn't print out a message similar
201 * to assert() and create a coredump if the user wants that. If not an error
202 * message is returned.
203 *
204 */
205 static enum test_return ensure(bool val, const char *expression, const char *file, int line)
206 {
207 if (!val)
208 {
209 if (verbose)
210 fprintf(stderr, "\n%s:%u: %s", file, line, expression);
211
212 if (do_core)
213 abort();
214
215 return TEST_FAIL;
216 }
217
218 return TEST_PASS;
219 }
220
221 #define verify(expression) do { if (ensure(expression, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
222 #define execute(expression) do { if (ensure(expression == TEST_PASS, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
223
224 /**
225 * Send a chunk of memory over the socket (retry if the call is iterrupted
226 */
227 static enum test_return retry_write(const void* buf, size_t len)
228 {
229 size_t offset= 0;
230 const char* ptr= buf;
231
232 do
233 {
234 size_t num_bytes= len - offset;
235 ssize_t nw= timeout_io_op(sock, POLLOUT, (void*)(ptr + offset), num_bytes);
236 if (nw == -1)
237 verify(errno == EINTR || errno == EAGAIN);
238 else
239 offset+= (size_t)nw;
240 } while (offset < len);
241
242 return TEST_PASS;
243 }
244
245 /**
246 * Resend a packet to the server (All fields in the command header should
247 * be in network byte order)
248 */
249 static enum test_return resend_packet(command *cmd)
250 {
251 size_t length= sizeof (protocol_binary_request_no_extras) +
252 ntohl(cmd->plain.message.header.request.bodylen);
253
254 execute(retry_write(cmd, length));
255 return TEST_PASS;
256 }
257
258 /**
259 * Send a command to the server. The command header needs to be updated
260 * to network byte order
261 */
262 static enum test_return send_packet(command *cmd)
263 {
264 /* Fix the byteorder of the header */
265 cmd->plain.message.header.request.keylen=
266 ntohs(cmd->plain.message.header.request.keylen);
267 cmd->plain.message.header.request.bodylen=
268 ntohl(cmd->plain.message.header.request.bodylen);
269 cmd->plain.message.header.request.cas=
270 ntohll(cmd->plain.message.header.request.cas);
271
272 execute(resend_packet(cmd));
273 return TEST_PASS;
274 }
275
276 /**
277 * Read a fixed length chunk of data from the server
278 */
279 static enum test_return retry_read(void *buf, size_t len)
280 {
281 size_t offset= 0;
282 do
283 {
284 ssize_t nr= timeout_io_op(sock, POLLIN, ((char*) buf) + offset, len - offset);
285 switch (nr) {
286 case -1 :
287 verify(errno == EINTR || errno == EAGAIN);
288 break;
289 case 0:
290 return TEST_FAIL;
291 default:
292 offset+= (size_t)nr;
293 }
294 } while (offset < len);
295
296 return TEST_PASS;
297 }
298
299 /**
300 * Receive a response from the server and conver the fields in the header
301 * to local byte order
302 */
303 static enum test_return recv_packet(response *rsp)
304 {
305 execute(retry_read(rsp, sizeof (protocol_binary_response_no_extras)));
306
307 /* Fix the byte order in the packet header */
308 rsp->plain.message.header.response.keylen=
309 ntohs(rsp->plain.message.header.response.keylen);
310 rsp->plain.message.header.response.status=
311 ntohs(rsp->plain.message.header.response.status);
312 rsp->plain.message.header.response.bodylen=
313 ntohl(rsp->plain.message.header.response.bodylen);
314 rsp->plain.message.header.response.cas=
315 ntohll(rsp->plain.message.header.response.cas);
316
317 size_t bodysz= rsp->plain.message.header.response.bodylen;
318 if (bodysz > 0)
319 execute(retry_read(rsp->bytes + sizeof (protocol_binary_response_no_extras), bodysz));
320
321 return TEST_PASS;
322 }
323
324 /**
325 * Create a storage command (add, set, replace etc)
326 *
327 * @param cmd destination buffer
328 * @param cc the storage command to create
329 * @param key the key to store
330 * @param keylen the length of the key
331 * @param dta the data to store with the key
332 * @param dtalen the length of the data to store with the key
333 * @param flags the flags to store along with the key
334 * @param exp the expiry time for the key
335 */
336 static void storage_command(command *cmd,
337 uint8_t cc,
338 const void* key,
339 size_t keylen,
340 const void* dta,
341 size_t dtalen,
342 uint32_t flags,
343 uint32_t exp)
344 {
345 /* all of the storage commands use the same command layout */
346 protocol_binary_request_set *request= &cmd->set;
347
348 memset(request, 0, sizeof (*request));
349 request->message.header.request.magic= PROTOCOL_BINARY_REQ;
350 request->message.header.request.opcode= cc;
351 request->message.header.request.keylen= (uint16_t)keylen;
352 request->message.header.request.extlen= 8;
353 request->message.header.request.bodylen= (uint32_t)(keylen + 8 + dtalen);
354 request->message.header.request.opaque= 0xdeadbeef;
355 request->message.body.flags= flags;
356 request->message.body.expiration= exp;
357
358 off_t key_offset= sizeof (protocol_binary_request_no_extras) + 8;
359 memcpy(cmd->bytes + key_offset, key, keylen);
360 if (dta != NULL)
361 memcpy(cmd->bytes + key_offset + keylen, dta, dtalen);
362 }
363
364 /**
365 * Create a basic command to send to the server
366 * @param cmd destination buffer
367 * @param cc the command to create
368 * @param key the key to store
369 * @param keylen the length of the key
370 * @param dta the data to store with the key
371 * @param dtalen the length of the data to store with the key
372 */
373 static void raw_command(command *cmd,
374 uint8_t cc,
375 const void* key,
376 size_t keylen,
377 const void* dta,
378 size_t dtalen)
379 {
380 /* all of the storage commands use the same command layout */
381 memset(cmd, 0, sizeof (*cmd));
382 cmd->plain.message.header.request.magic= PROTOCOL_BINARY_REQ;
383 cmd->plain.message.header.request.opcode= cc;
384 cmd->plain.message.header.request.keylen= (uint16_t)keylen;
385 cmd->plain.message.header.request.bodylen= (uint32_t)(keylen + dtalen);
386 cmd->plain.message.header.request.opaque= 0xdeadbeef;
387
388 off_t key_offset= sizeof (protocol_binary_request_no_extras);
389
390 if (key != NULL)
391 memcpy(cmd->bytes + key_offset, key, keylen);
392
393 if (dta != NULL)
394 memcpy(cmd->bytes + key_offset + keylen, dta, dtalen);
395 }
396
397 /**
398 * Create the flush command
399 * @param cmd destination buffer
400 * @param cc the command to create (FLUSH/FLUSHQ)
401 * @param exptime when to flush
402 * @param use_extra to force using of the extra field?
403 */
404 static void flush_command(command *cmd,
405 uint8_t cc, uint32_t exptime, bool use_extra)
406 {
407 memset(cmd, 0, sizeof (cmd->flush));
408 cmd->flush.message.header.request.magic= PROTOCOL_BINARY_REQ;
409 cmd->flush.message.header.request.opcode= cc;
410 cmd->flush.message.header.request.opaque= 0xdeadbeef;
411
412 if (exptime != 0 || use_extra)
413 {
414 cmd->flush.message.header.request.extlen= 4;
415 cmd->flush.message.body.expiration= htonl(exptime);
416 cmd->flush.message.header.request.bodylen= 4;
417 }
418 }
419
420 /**
421 * Create a incr/decr command
422 * @param cc the cmd to create (FLUSH/FLUSHQ)
423 * @param key the key to operate on
424 * @param keylen the number of bytes in the key
425 * @param delta the number to add/subtract
426 * @param initial the initial value if the key doesn't exist
427 * @param exp when the key should expire if it isn't set
428 */
429 static void arithmetic_command(command *cmd,
430 uint8_t cc,
431 const void* key,
432 size_t keylen,
433 uint64_t delta,
434 uint64_t initial,
435 uint32_t exp)
436 {
437 memset(cmd, 0, sizeof (cmd->incr));
438 cmd->incr.message.header.request.magic= PROTOCOL_BINARY_REQ;
439 cmd->incr.message.header.request.opcode= cc;
440 cmd->incr.message.header.request.keylen= (uint16_t)keylen;
441 cmd->incr.message.header.request.extlen= 20;
442 cmd->incr.message.header.request.bodylen= (uint32_t)(keylen + 20);
443 cmd->incr.message.header.request.opaque= 0xdeadbeef;
444 cmd->incr.message.body.delta= htonll(delta);
445 cmd->incr.message.body.initial= htonll(initial);
446 cmd->incr.message.body.expiration= htonl(exp);
447
448 off_t key_offset= sizeof (protocol_binary_request_no_extras) + 20;
449 memcpy(cmd->bytes + key_offset, key, keylen);
450 }
451
452 /**
453 * Validate the response header from the server
454 * @param rsp the response to check
455 * @param cc the expected command
456 * @param status the expected status
457 */
458 static enum test_return do_validate_response_header(response *rsp,
459 uint8_t cc, uint16_t status)
460 {
461 verify(rsp->plain.message.header.response.magic == PROTOCOL_BINARY_RES);
462 verify(rsp->plain.message.header.response.opcode == cc);
463 verify(rsp->plain.message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
464 verify(rsp->plain.message.header.response.status == status);
465 verify(rsp->plain.message.header.response.opaque == 0xdeadbeef);
466
467 if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS)
468 {
469 switch (cc) {
470 case PROTOCOL_BINARY_CMD_ADDQ:
471 case PROTOCOL_BINARY_CMD_APPENDQ:
472 case PROTOCOL_BINARY_CMD_DECREMENTQ:
473 case PROTOCOL_BINARY_CMD_DELETEQ:
474 case PROTOCOL_BINARY_CMD_FLUSHQ:
475 case PROTOCOL_BINARY_CMD_INCREMENTQ:
476 case PROTOCOL_BINARY_CMD_PREPENDQ:
477 case PROTOCOL_BINARY_CMD_QUITQ:
478 case PROTOCOL_BINARY_CMD_REPLACEQ:
479 case PROTOCOL_BINARY_CMD_SETQ:
480 verify("Quiet command shouldn't return on success" == NULL);
481 default:
482 break;
483 }
484
485 switch (cc) {
486 case PROTOCOL_BINARY_CMD_ADD:
487 case PROTOCOL_BINARY_CMD_REPLACE:
488 case PROTOCOL_BINARY_CMD_SET:
489 case PROTOCOL_BINARY_CMD_APPEND:
490 case PROTOCOL_BINARY_CMD_PREPEND:
491 verify(rsp->plain.message.header.response.keylen == 0);
492 verify(rsp->plain.message.header.response.extlen == 0);
493 verify(rsp->plain.message.header.response.bodylen == 0);
494 verify(rsp->plain.message.header.response.cas != 0);
495 break;
496 case PROTOCOL_BINARY_CMD_FLUSH:
497 case PROTOCOL_BINARY_CMD_NOOP:
498 case PROTOCOL_BINARY_CMD_QUIT:
499 case PROTOCOL_BINARY_CMD_DELETE:
500 verify(rsp->plain.message.header.response.keylen == 0);
501 verify(rsp->plain.message.header.response.extlen == 0);
502 verify(rsp->plain.message.header.response.bodylen == 0);
503 verify(rsp->plain.message.header.response.cas == 0);
504 break;
505
506 case PROTOCOL_BINARY_CMD_DECREMENT:
507 case PROTOCOL_BINARY_CMD_INCREMENT:
508 verify(rsp->plain.message.header.response.keylen == 0);
509 verify(rsp->plain.message.header.response.extlen == 0);
510 verify(rsp->plain.message.header.response.bodylen == 8);
511 verify(rsp->plain.message.header.response.cas != 0);
512 break;
513
514 case PROTOCOL_BINARY_CMD_STAT:
515 verify(rsp->plain.message.header.response.extlen == 0);
516 /* key and value exists in all packets except in the terminating */
517 verify(rsp->plain.message.header.response.cas == 0);
518 break;
519
520 case PROTOCOL_BINARY_CMD_VERSION:
521 verify(rsp->plain.message.header.response.keylen == 0);
522 verify(rsp->plain.message.header.response.extlen == 0);
523 verify(rsp->plain.message.header.response.bodylen != 0);
524 verify(rsp->plain.message.header.response.cas == 0);
525 break;
526
527 case PROTOCOL_BINARY_CMD_GET:
528 case PROTOCOL_BINARY_CMD_GETQ:
529 verify(rsp->plain.message.header.response.keylen == 0);
530 verify(rsp->plain.message.header.response.extlen == 4);
531 verify(rsp->plain.message.header.response.cas != 0);
532 break;
533
534 case PROTOCOL_BINARY_CMD_GETK:
535 case PROTOCOL_BINARY_CMD_GETKQ:
536 verify(rsp->plain.message.header.response.keylen != 0);
537 verify(rsp->plain.message.header.response.extlen == 4);
538 verify(rsp->plain.message.header.response.cas != 0);
539 break;
540
541 default:
542 /* Undefined command code */
543 break;
544 }
545 }
546 else
547 {
548 verify(rsp->plain.message.header.response.cas == 0);
549 verify(rsp->plain.message.header.response.extlen == 0);
550 if (cc != PROTOCOL_BINARY_CMD_GETK)
551 {
552 verify(rsp->plain.message.header.response.keylen == 0);
553 }
554 }
555
556 return TEST_PASS;
557 }
558
559 /* We call verify(validate_response_header), but that macro
560 * expects a boolean expression, and the function returns
561 * an enum.... Let's just create a macro to avoid cluttering
562 * the code with all of the == TEST_PASS ;-)
563 */
564 #define validate_response_header(a,b,c) \
565 do_validate_response_header(a,b,c) == TEST_PASS
566
567 static enum test_return test_binary_noop(void)
568 {
569 command cmd;
570 response rsp;
571 raw_command(&cmd, PROTOCOL_BINARY_CMD_NOOP, NULL, 0, NULL, 0);
572 execute(send_packet(&cmd));
573 execute(recv_packet(&rsp));
574 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_NOOP,
575 PROTOCOL_BINARY_RESPONSE_SUCCESS));
576 return TEST_PASS;
577 }
578
579 static enum test_return test_binary_quit_impl(uint8_t cc)
580 {
581 command cmd;
582 response rsp;
583 raw_command(&cmd, cc, NULL, 0, NULL, 0);
584
585 execute(send_packet(&cmd));
586 if (cc == PROTOCOL_BINARY_CMD_QUIT)
587 {
588 execute(recv_packet(&rsp));
589 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_QUIT,
590 PROTOCOL_BINARY_RESPONSE_SUCCESS));
591 }
592
593 /* Socket should be closed now, read should return 0 */
594 verify(timeout_io_op(sock, POLLIN, rsp.bytes, sizeof(rsp.bytes)) == 0);
595
596 return TEST_PASS_RECONNECT;
597 }
598
599 static enum test_return test_binary_quit(void)
600 {
601 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
602 }
603
604 static enum test_return test_binary_quitq(void)
605 {
606 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
607 }
608
609 static enum test_return test_binary_set_impl(const char* key, uint8_t cc)
610 {
611 command cmd;
612 response rsp;
613
614 uint64_t value= 0xdeadbeefdeadcafe;
615 storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
616
617 /* set should always work */
618 for (int ii= 0; ii < 10; ii++)
619 {
620 if (ii == 0)
621 execute(send_packet(&cmd));
622 else
623 execute(resend_packet(&cmd));
624
625 if (cc == PROTOCOL_BINARY_CMD_SET)
626 {
627 execute(recv_packet(&rsp));
628 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
629 }
630 else
631 execute(test_binary_noop());
632 }
633
634 /*
635 * We need to get the current CAS id, and at this time we haven't
636 * verified that we have a working get
637 */
638 if (cc == PROTOCOL_BINARY_CMD_SETQ)
639 {
640 cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SET;
641 execute(resend_packet(&cmd));
642 execute(recv_packet(&rsp));
643 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
644 PROTOCOL_BINARY_RESPONSE_SUCCESS));
645 cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ;
646 }
647
648 /* try to set with the correct CAS value */
649 cmd.plain.message.header.request.cas=
650 htonll(rsp.plain.message.header.response.cas);
651 execute(resend_packet(&cmd));
652 if (cc == PROTOCOL_BINARY_CMD_SET)
653 {
654 execute(recv_packet(&rsp));
655 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
656 }
657 else
658 execute(test_binary_noop());
659
660 /* try to set with an incorrect CAS value */
661 cmd.plain.message.header.request.cas=
662 htonll(rsp.plain.message.header.response.cas - 1);
663 execute(resend_packet(&cmd));
664 execute(recv_packet(&rsp));
665 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
666
667 return test_binary_noop();
668 }
669
670 static enum test_return test_binary_set(void)
671 {
672 return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
673 }
674
675 static enum test_return test_binary_setq(void)
676 {
677 return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
678 }
679
680 static enum test_return test_binary_add_impl(const char* key, uint8_t cc)
681 {
682 command cmd;
683 response rsp;
684 uint64_t value= 0xdeadbeefdeadcafe;
685 storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
686
687 /* first add should work, rest of them should fail (even with cas
688 as wildcard */
689 for (int ii=0; ii < 10; ii++)
690 {
691 if (ii == 0)
692 execute(send_packet(&cmd));
693 else
694 execute(resend_packet(&cmd));
695
696 if (cc == PROTOCOL_BINARY_CMD_ADD || ii > 0)
697 {
698 uint16_t expected_result;
699 if (ii == 0)
700 expected_result= PROTOCOL_BINARY_RESPONSE_SUCCESS;
701 else
702 expected_result= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
703
704 execute(recv_packet(&rsp));
705 verify(validate_response_header(&rsp, cc, expected_result));
706 }
707 else
708 execute(test_binary_noop());
709 }
710
711 return TEST_PASS;
712 }
713
714 static enum test_return test_binary_add(void)
715 {
716 return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
717 }
718
719 static enum test_return test_binary_addq(void)
720 {
721 return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
722 }
723
724 static enum test_return binary_set_item(const char *key, const char *value)
725 {
726 command cmd;
727 response rsp;
728 storage_command(&cmd, PROTOCOL_BINARY_CMD_SET, key, strlen(key),
729 value, strlen(value), 0, 0);
730 execute(send_packet(&cmd));
731 execute(recv_packet(&rsp));
732 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
733 PROTOCOL_BINARY_RESPONSE_SUCCESS));
734 return TEST_PASS;
735 }
736
737 static enum test_return test_binary_replace_impl(const char* key, uint8_t cc)
738 {
739 command cmd;
740 response rsp;
741 uint64_t value= 0xdeadbeefdeadcafe;
742 storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
743
744 /* first replace should fail, successive should succeed (when the
745 item is added! */
746 for (int ii= 0; ii < 10; ii++)
747 {
748 if (ii == 0)
749 execute(send_packet(&cmd));
750 else
751 execute(resend_packet(&cmd));
752
753 if (cc == PROTOCOL_BINARY_CMD_REPLACE || ii == 0)
754 {
755 uint16_t expected_result;
756 if (ii == 0)
757 expected_result=PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
758 else
759 expected_result=PROTOCOL_BINARY_RESPONSE_SUCCESS;
760
761 execute(recv_packet(&rsp));
762 verify(validate_response_header(&rsp, cc, expected_result));
763
764 if (ii == 0)
765 execute(binary_set_item(key, key));
766 }
767 else
768 execute(test_binary_noop());
769 }
770
771 /* verify that replace with CAS value works! */
772 cmd.plain.message.header.request.cas=
773 htonll(rsp.plain.message.header.response.cas);
774 execute(resend_packet(&cmd));
775
776 if (cc == PROTOCOL_BINARY_CMD_REPLACE)
777 {
778 execute(recv_packet(&rsp));
779 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
780 }
781 else
782 execute(test_binary_noop());
783
784 /* try to set with an incorrect CAS value */
785 cmd.plain.message.header.request.cas=
786 htonll(rsp.plain.message.header.response.cas - 1);
787 execute(resend_packet(&cmd));
788 execute(recv_packet(&rsp));
789 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
790
791 return TEST_PASS;
792 }
793
794 static enum test_return test_binary_replace(void)
795 {
796 return test_binary_replace_impl("test_binary_replace", PROTOCOL_BINARY_CMD_REPLACE);
797 }
798
799 static enum test_return test_binary_replaceq(void)
800 {
801 return test_binary_replace_impl("test_binary_replaceq", PROTOCOL_BINARY_CMD_REPLACEQ);
802 }
803
804 static enum test_return test_binary_delete_impl(const char *key, uint8_t cc)
805 {
806 command cmd;
807 response rsp;
808 raw_command(&cmd, cc, key, strlen(key), NULL, 0);
809
810 /* The delete shouldn't work the first time, because the item isn't there */
811 execute(send_packet(&cmd));
812 execute(recv_packet(&rsp));
813 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
814 execute(binary_set_item(key, key));
815
816 /* The item should be present now, resend*/
817 execute(resend_packet(&cmd));
818 if (cc == PROTOCOL_BINARY_CMD_DELETE)
819 {
820 execute(recv_packet(&rsp));
821 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
822 }
823
824 execute(test_binary_noop());
825
826 return TEST_PASS;
827 }
828
829 static enum test_return test_binary_delete(void)
830 {
831 return test_binary_delete_impl("test_binary_delete", PROTOCOL_BINARY_CMD_DELETE);
832 }
833
834 static enum test_return test_binary_deleteq(void)
835 {
836 return test_binary_delete_impl("test_binary_deleteq", PROTOCOL_BINARY_CMD_DELETEQ);
837 }
838
839 static enum test_return test_binary_get_impl(const char *key, uint8_t cc)
840 {
841 command cmd;
842 response rsp;
843
844 raw_command(&cmd, cc, key, strlen(key), NULL, 0);
845 execute(send_packet(&cmd));
846
847 if (cc == PROTOCOL_BINARY_CMD_GET || cc == PROTOCOL_BINARY_CMD_GETK)
848 {
849 execute(recv_packet(&rsp));
850 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
851 }
852 else
853 execute(test_binary_noop());
854
855 execute(binary_set_item(key, key));
856 execute(resend_packet(&cmd));
857 execute(recv_packet(&rsp));
858 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
859
860 return TEST_PASS;
861 }
862
863 static enum test_return test_binary_get(void)
864 {
865 return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
866 }
867
868 static enum test_return test_binary_getk(void)
869 {
870 return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
871 }
872
873 static enum test_return test_binary_getq(void)
874 {
875 return test_binary_get_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
876 }
877
878 static enum test_return test_binary_getkq(void)
879 {
880 return test_binary_get_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
881 }
882
883 static enum test_return test_binary_incr_impl(const char* key, uint8_t cc)
884 {
885 command cmd;
886 response rsp;
887 arithmetic_command(&cmd, cc, key, strlen(key), 1, 0, 0);
888
889 uint64_t ii;
890 for (ii= 0; ii < 10; ++ii)
891 {
892 if (ii == 0)
893 execute(send_packet(&cmd));
894 else
895 execute(resend_packet(&cmd));
896
897 if (cc == PROTOCOL_BINARY_CMD_INCREMENT)
898 {
899 execute(recv_packet(&rsp));
900 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
901 verify(ntohll(rsp.incr.message.body.value) == ii);
902 }
903 else
904 execute(test_binary_noop());
905 }
906
907 /* @todo add incorrect CAS */
908 return TEST_PASS;
909 }
910
911 static enum test_return test_binary_incr(void)
912 {
913 return test_binary_incr_impl("test_binary_incr", PROTOCOL_BINARY_CMD_INCREMENT);
914 }
915
916 static enum test_return test_binary_incrq(void)
917 {
918 return test_binary_incr_impl("test_binary_incrq", PROTOCOL_BINARY_CMD_INCREMENTQ);
919 }
920
921 static enum test_return test_binary_decr_impl(const char* key, uint8_t cc)
922 {
923 command cmd;
924 response rsp;
925 arithmetic_command(&cmd, cc, key, strlen(key), 1, 9, 0);
926
927 int ii;
928 for (ii= 9; ii > -1; --ii)
929 {
930 if (ii == 9)
931 execute(send_packet(&cmd));
932 else
933 execute(resend_packet(&cmd));
934
935 if (cc == PROTOCOL_BINARY_CMD_DECREMENT)
936 {
937 execute(recv_packet(&rsp));
938 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
939 verify(ntohll(rsp.decr.message.body.value) == (uint64_t)ii);
940 }
941 else
942 execute(test_binary_noop());
943 }
944
945 /* decr 0 should not wrap */
946 execute(resend_packet(&cmd));
947 if (cc == PROTOCOL_BINARY_CMD_DECREMENT)
948 {
949 execute(recv_packet(&rsp));
950 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
951 verify(ntohll(rsp.decr.message.body.value) == 0);
952 }
953 else
954 {
955 /* @todo get the value and verify! */
956
957 }
958
959 /* @todo add incorrect cas */
960 execute(test_binary_noop());
961 return TEST_PASS;
962 }
963
964 static enum test_return test_binary_decr(void)
965 {
966 return test_binary_decr_impl("test_binary_decr",
967 PROTOCOL_BINARY_CMD_DECREMENT);
968 }
969
970 static enum test_return test_binary_decrq(void)
971 {
972 return test_binary_decr_impl("test_binary_decrq",
973 PROTOCOL_BINARY_CMD_DECREMENTQ);
974 }
975
976 static enum test_return test_binary_version(void)
977 {
978 command cmd;
979 response rsp;
980 raw_command(&cmd, PROTOCOL_BINARY_CMD_VERSION, NULL, 0, NULL, 0);
981
982 execute(send_packet(&cmd));
983 execute(recv_packet(&rsp));
984 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_VERSION,
985 PROTOCOL_BINARY_RESPONSE_SUCCESS));
986
987 return TEST_PASS;
988 }
989
990 static enum test_return test_binary_flush_impl(const char *key, uint8_t cc)
991 {
992 command cmd;
993 response rsp;
994
995 for (int ii= 0; ii < 2; ++ii)
996 {
997 execute(binary_set_item(key, key));
998 flush_command(&cmd, cc, 0, ii == 0);
999 execute(send_packet(&cmd));
1000
1001 if (cc == PROTOCOL_BINARY_CMD_FLUSH)
1002 {
1003 execute(recv_packet(&rsp));
1004 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
1005 }
1006 else
1007 execute(test_binary_noop());
1008
1009 raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
1010 execute(send_packet(&cmd));
1011 execute(recv_packet(&rsp));
1012 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET,
1013 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
1014 }
1015
1016 return TEST_PASS;
1017 }
1018
1019 static enum test_return test_binary_flush(void)
1020 {
1021 return test_binary_flush_impl("test_binary_flush", PROTOCOL_BINARY_CMD_FLUSH);
1022 }
1023
1024 static enum test_return test_binary_flushq(void)
1025 {
1026 return test_binary_flush_impl("test_binary_flushq", PROTOCOL_BINARY_CMD_FLUSHQ);
1027 }
1028
1029 static enum test_return test_binary_concat_impl(const char *key, uint8_t cc)
1030 {
1031 command cmd;
1032 response rsp;
1033 const char *value;
1034
1035 if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
1036 value="hello";
1037 else
1038 value=" world";
1039
1040 execute(binary_set_item(key, value));
1041
1042 if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
1043 value=" world";
1044 else
1045 value="hello";
1046
1047 raw_command(&cmd, cc, key, strlen(key), value, strlen(value));
1048 execute(send_packet(&cmd));
1049 if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_PREPEND)
1050 {
1051 execute(recv_packet(&rsp));
1052 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
1053 }
1054 else
1055 execute(test_binary_noop());
1056
1057 raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
1058 execute(send_packet(&cmd));
1059 execute(recv_packet(&rsp));
1060 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET,
1061 PROTOCOL_BINARY_RESPONSE_SUCCESS));
1062 verify(rsp.plain.message.header.response.bodylen - 4 == 11);
1063 verify(memcmp(rsp.bytes + 28, "hello world", 11) == 0);
1064
1065 return TEST_PASS;
1066 }
1067
1068 static enum test_return test_binary_append(void)
1069 {
1070 return test_binary_concat_impl("test_binary_append", PROTOCOL_BINARY_CMD_APPEND);
1071 }
1072
1073 static enum test_return test_binary_prepend(void)
1074 {
1075 return test_binary_concat_impl("test_binary_prepend", PROTOCOL_BINARY_CMD_PREPEND);
1076 }
1077
1078 static enum test_return test_binary_appendq(void)
1079 {
1080 return test_binary_concat_impl("test_binary_appendq", PROTOCOL_BINARY_CMD_APPENDQ);
1081 }
1082
1083 static enum test_return test_binary_prependq(void)
1084 {
1085 return test_binary_concat_impl("test_binary_prependq", PROTOCOL_BINARY_CMD_PREPENDQ);
1086 }
1087
1088 static enum test_return test_binary_stat(void)
1089 {
1090 command cmd;
1091 response rsp;
1092
1093 raw_command(&cmd, PROTOCOL_BINARY_CMD_STAT, NULL, 0, NULL, 0);
1094 execute(send_packet(&cmd));
1095
1096 do
1097 {
1098 execute(recv_packet(&rsp));
1099 verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_STAT,
1100 PROTOCOL_BINARY_RESPONSE_SUCCESS));
1101 } while (rsp.plain.message.header.response.keylen != 0);
1102
1103 return TEST_PASS;
1104 }
1105
1106 static enum test_return test_binary_illegal(void)
1107 {
1108 command cmd;
1109 response rsp;
1110 uint8_t cc= 0x1b;
1111
1112 while (cc != 0x00)
1113 {
1114 raw_command(&cmd, cc, NULL, 0, NULL, 0);
1115 execute(send_packet(&cmd));
1116 execute(recv_packet(&rsp));
1117 verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND));
1118 ++cc;
1119 }
1120
1121 return TEST_PASS_RECONNECT;
1122 }
1123
1124 static enum test_return send_string(const char *cmd)
1125 {
1126 execute(retry_write(cmd, strlen(cmd)));
1127 return TEST_PASS;
1128 }
1129
1130 static enum test_return receive_line(char *buffer, size_t size)
1131 {
1132 size_t offset= 0;
1133 while (offset < size)
1134 {
1135 execute(retry_read(buffer + offset, 1));
1136 if (buffer[offset] == '\n')
1137 {
1138 if (offset + 1 < size)
1139 {
1140 buffer[offset + 1]= '\0';
1141 return TEST_PASS;
1142 }
1143 else
1144 return TEST_FAIL;
1145 }
1146 ++offset;
1147 }
1148
1149 return TEST_FAIL;
1150 }
1151
1152 static enum test_return receive_response(const char *msg) {
1153 char buffer[80];
1154 execute(receive_line(buffer, sizeof(buffer)));
1155 verify(strcmp(msg, buffer) == 0);
1156 return TEST_PASS;
1157 }
1158
1159 static enum test_return test_ascii_quit(void)
1160 {
1161 /* Verify that quit handles unknown options */
1162 execute(send_string("quit foo bar\r\n"));
1163 execute(receive_response("ERROR\r\n"));
1164
1165 /* quit doesn't support noreply */
1166 execute(send_string("quit noreply\r\n"));
1167 execute(receive_response("ERROR\r\n"));
1168
1169 /* Verify that quit works */
1170 execute(send_string("quit\r\n"));
1171
1172 /* Socket should be closed now, read should return 0 */
1173 char buffer[80];
1174 verify(timeout_io_op(sock, POLLIN, buffer, sizeof(buffer)) == 0);
1175 return TEST_PASS_RECONNECT;
1176
1177 }
1178
1179 static enum test_return test_ascii_version(void)
1180 {
1181 /* Verify that version command handles unknown options */
1182 execute(send_string("version foo bar\r\n"));
1183 execute(receive_response("ERROR\r\n"));
1184
1185 /* version doesn't support noreply */
1186 execute(send_string("version noreply\r\n"));
1187 execute(receive_response("ERROR\r\n"));
1188
1189 /* Verify that verify works */
1190 execute(send_string("version\r\n"));
1191 char buffer[256];
1192 execute(receive_line(buffer, sizeof(buffer)));
1193 verify(strncmp(buffer, "VERSION ", 8) == 0);
1194
1195 return TEST_PASS;
1196 }
1197
1198 static enum test_return test_ascii_verbosity(void)
1199 {
1200 /* This command does not adhere to the spec! */
1201 execute(send_string("verbosity foo bar my\r\n"));
1202 execute(receive_response("ERROR\r\n"));
1203
1204 execute(send_string("verbosity noreply\r\n"));
1205 execute(test_ascii_version());
1206
1207 execute(send_string("verbosity 0 noreply\r\n"));
1208 execute(test_ascii_version());
1209
1210 execute(send_string("verbosity\r\n"));
1211 execute(receive_response("ERROR\r\n"));
1212
1213 execute(send_string("verbosity 1\r\n"));
1214 execute(receive_response("OK\r\n"));
1215
1216 execute(send_string("verbosity 0\r\n"));
1217 execute(receive_response("OK\r\n"));
1218
1219 return TEST_PASS;
1220 }
1221
1222
1223
1224 static enum test_return test_ascii_set_impl(const char* key, bool noreply)
1225 {
1226 /* @todo add tests for bogus format! */
1227 char buffer[1024];
1228 sprintf(buffer, "set %s 0 0 5%s\r\nvalue\r\n", key,
1229 noreply ? " noreply" : "");
1230 execute(send_string(buffer));
1231
1232 if (!noreply)
1233 execute(receive_response("STORED\r\n"));
1234
1235 return test_ascii_version();
1236 }
1237
1238 static enum test_return test_ascii_set(void)
1239 {
1240 return test_ascii_set_impl("test_ascii_set", false);
1241 }
1242
1243 static enum test_return test_ascii_set_noreply(void)
1244 {
1245 return test_ascii_set_impl("test_ascii_set_noreply", true);
1246 }
1247
1248 static enum test_return test_ascii_add_impl(const char* key, bool noreply)
1249 {
1250 /* @todo add tests for bogus format! */
1251 char buffer[1024];
1252 sprintf(buffer, "add %s 0 0 5%s\r\nvalue\r\n", key,
1253 noreply ? " noreply" : "");
1254 execute(send_string(buffer));
1255
1256 if (!noreply)
1257 execute(receive_response("STORED\r\n"));
1258
1259 execute(send_string(buffer));
1260
1261 if (!noreply)
1262 execute(receive_response("NOT_STORED\r\n"));
1263
1264 return test_ascii_version();
1265 }
1266
1267 static enum test_return test_ascii_add(void)
1268 {
1269 return test_ascii_add_impl("test_ascii_add", false);
1270 }
1271
1272 static enum test_return test_ascii_add_noreply(void)
1273 {
1274 return test_ascii_add_impl("test_ascii_add_noreply", true);
1275 }
1276
1277 static enum test_return ascii_get_value(const char *key, const char *value)
1278 {
1279
1280 char buffer[1024];
1281 size_t datasize= strlen(value);
1282
1283 verify(datasize < sizeof(buffer));
1284 execute(receive_line(buffer, sizeof(buffer)));
1285 verify(strncmp(buffer, "VALUE ", 6) == 0);
1286 verify(strncmp(buffer + 6, key, strlen(key)) == 0);
1287 char *ptr= buffer + 6 + strlen(key) + 1;
1288 char *end;
1289
1290 unsigned long val= strtoul(ptr, &end, 10); /* flags */
1291 verify(ptr != end);
1292 verify(val == 0);
1293 verify(end != NULL);
1294 val= strtoul(end, &end, 10); /* size */
1295 verify(ptr != end);
1296 verify(val == datasize);
1297 verify(end != NULL);
1298 while (*end != '\n' && isspace(*end))
1299 ++end;
1300 verify(*end == '\n');
1301
1302 execute(retry_read(buffer, datasize));
1303 verify(memcmp(buffer, value, datasize) == 0);
1304
1305 execute(retry_read(buffer, 2));
1306 verify(memcmp(buffer, "\r\n", 2) == 0);
1307
1308 return TEST_PASS;
1309 }
1310
1311 static enum test_return ascii_get_item(const char *key, const char *value,
1312 bool exist)
1313 {
1314 char buffer[1024];
1315 size_t datasize= 0;
1316 if (value != NULL)
1317 datasize= strlen(value);
1318
1319 verify(datasize < sizeof(buffer));
1320 sprintf(buffer, "get %s\r\n", key);
1321 execute(send_string(buffer));
1322
1323 if (exist)
1324 execute(ascii_get_value(key, value));
1325
1326 execute(retry_read(buffer, 5));
1327 verify(memcmp(buffer, "END\r\n", 5) == 0);
1328
1329 return TEST_PASS;
1330 }
1331
1332 static enum test_return ascii_gets_value(const char *key, const char *value,
1333 unsigned long *cas)
1334 {
1335
1336 char buffer[1024];
1337 size_t datasize= strlen(value);
1338
1339 verify(datasize < sizeof(buffer));
1340 execute(receive_line(buffer, sizeof(buffer)));
1341 verify(strncmp(buffer, "VALUE ", 6) == 0);
1342 verify(strncmp(buffer + 6, key, strlen(key)) == 0);
1343 char *ptr= buffer + 6 + strlen(key) + 1;
1344 char *end;
1345
1346 unsigned long val= strtoul(ptr, &end, 10); /* flags */
1347 verify(ptr != end);
1348 verify(val == 0);
1349 verify(end != NULL);
1350 val= strtoul(end, &end, 10); /* size */
1351 verify(ptr != end);
1352 verify(val == datasize);
1353 verify(end != NULL);
1354 *cas= strtoul(end, &end, 10); /* cas */
1355 verify(ptr != end);
1356 verify(val == datasize);
1357 verify(end != NULL);
1358
1359 while (*end != '\n' && isspace(*end))
1360 ++end;
1361 verify(*end == '\n');
1362
1363 execute(retry_read(buffer, datasize));
1364 verify(memcmp(buffer, value, datasize) == 0);
1365
1366 execute(retry_read(buffer, 2));
1367 verify(memcmp(buffer, "\r\n", 2) == 0);
1368
1369 return TEST_PASS;
1370 }
1371
1372 static enum test_return ascii_gets_item(const char *key, const char *value,
1373 bool exist, unsigned long *cas)
1374 {
1375 char buffer[1024];
1376 size_t datasize= 0;
1377 if (value != NULL)
1378 datasize= strlen(value);
1379
1380 verify(datasize < sizeof(buffer));
1381 sprintf(buffer, "gets %s\r\n", key);
1382 execute(send_string(buffer));
1383
1384 if (exist)
1385 execute(ascii_gets_value(key, value, cas));
1386
1387 execute(retry_read(buffer, 5));
1388 verify(memcmp(buffer, "END\r\n", 5) == 0);
1389
1390 return TEST_PASS;
1391 }
1392
1393 static enum test_return ascii_set_item(const char *key, const char *value)
1394 {
1395 char buffer[300];
1396 size_t len= strlen(value);
1397 sprintf(buffer, "set %s 0 0 %u\r\n", key, (unsigned int)len);
1398 execute(send_string(buffer));
1399 execute(retry_write(value, len));
1400 execute(send_string("\r\n"));
1401 execute(receive_response("STORED\r\n"));
1402 return TEST_PASS;
1403 }
1404
1405 static enum test_return test_ascii_replace_impl(const char* key, bool noreply)
1406 {
1407 char buffer[1024];
1408 sprintf(buffer, "replace %s 0 0 5%s\r\nvalue\r\n", key,
1409 noreply ? " noreply" : "");
1410 execute(send_string(buffer));
1411
1412 if (noreply)
1413 execute(test_ascii_version());
1414 else
1415 execute(receive_response("NOT_STORED\r\n"));
1416
1417 execute(ascii_set_item(key, "value"));
1418 execute(ascii_get_item(key, "value", true));
1419
1420
1421 execute(send_string(buffer));
1422
1423 if (noreply)
1424 execute(test_ascii_version());
1425 else
1426 execute(receive_response("STORED\r\n"));
1427
1428 return test_ascii_version();
1429 }
1430
1431 static enum test_return test_ascii_replace(void)
1432 {
1433 return test_ascii_replace_impl("test_ascii_replace", false);
1434 }
1435
1436 static enum test_return test_ascii_replace_noreply(void)
1437 {
1438 return test_ascii_replace_impl("test_ascii_replace_noreply", true);
1439 }
1440
1441 static enum test_return test_ascii_cas_impl(const char* key, bool noreply)
1442 {
1443 char buffer[1024];
1444 unsigned long cas;
1445
1446 execute(ascii_set_item(key, "value"));
1447 execute(ascii_gets_item(key, "value", true, &cas));
1448
1449 sprintf(buffer, "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas,
1450 noreply ? " noreply" : "");
1451 execute(send_string(buffer));
1452
1453 if (noreply)
1454 execute(test_ascii_version());
1455 else
1456 execute(receive_response("STORED\r\n"));
1457
1458 /* reexecute the same command should fail due to illegal cas */
1459 execute(send_string(buffer));
1460
1461 if (noreply)
1462 execute(test_ascii_version());
1463 else
1464 execute(receive_response("EXISTS\r\n"));
1465
1466 return test_ascii_version();
1467 }
1468
1469 static enum test_return test_ascii_cas(void)
1470 {
1471 return test_ascii_cas_impl("test_ascii_cas", false);
1472 }
1473
1474 static enum test_return test_ascii_cas_noreply(void)
1475 {
1476 return test_ascii_cas_impl("test_ascii_cas_noreply", true);
1477 }
1478
1479 static enum test_return test_ascii_delete_impl(const char *key, bool noreply)
1480 {
1481 execute(ascii_set_item(key, "value"));
1482
1483 execute(send_string("delete\r\n"));
1484 execute(receive_response("ERROR\r\n"));
1485 /* BUG: the server accepts delete a b */
1486 execute(send_string("delete a b c d e\r\n"));
1487 execute(receive_response("ERROR\r\n"));
1488
1489 char buffer[1024];
1490 sprintf(buffer, "delete %s%s\r\n", key, noreply ? " noreply" : "");
1491 execute(send_string(buffer));
1492
1493 if (noreply)
1494 execute(test_ascii_version());
1495 else
1496 execute(receive_response("DELETED\r\n"));
1497
1498 execute(ascii_get_item(key, "value", false));
1499 execute(send_string(buffer));
1500 if (noreply)
1501 execute(test_ascii_version());
1502 else
1503 execute(receive_response("NOT_FOUND\r\n"));
1504
1505 return TEST_PASS;
1506 }
1507
1508 static enum test_return test_ascii_delete(void)
1509 {
1510 return test_ascii_delete_impl("test_ascii_delete", false);
1511 }
1512
1513 static enum test_return test_ascii_delete_noreply(void)
1514 {
1515 return test_ascii_delete_impl("test_ascii_delete_noreply", true);
1516 }
1517
1518 static enum test_return test_ascii_get(void)
1519 {
1520 execute(ascii_set_item("test_ascii_get", "value"));
1521
1522 execute(send_string("get\r\n"));
1523 execute(receive_response("ERROR\r\n"));
1524 execute(ascii_get_item("test_ascii_get", "value", true));
1525 execute(ascii_get_item("test_ascii_get_notfound", "value", false));
1526
1527 return TEST_PASS;
1528 }
1529
1530 static enum test_return test_ascii_gets(void)
1531 {
1532 execute(ascii_set_item("test_ascii_gets", "value"));
1533
1534 execute(send_string("gets\r\n"));
1535 execute(receive_response("ERROR\r\n"));
1536 unsigned long cas;
1537 execute(ascii_gets_item("test_ascii_gets", "value", true, &cas));
1538 execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas));
1539
1540 return TEST_PASS;
1541 }
1542
1543 static enum test_return test_ascii_mget(void)
1544 {
1545 execute(ascii_set_item("test_ascii_mget1", "value"));
1546 execute(ascii_set_item("test_ascii_mget2", "value"));
1547 execute(ascii_set_item("test_ascii_mget3", "value"));
1548 execute(ascii_set_item("test_ascii_mget4", "value"));
1549 execute(ascii_set_item("test_ascii_mget5", "value"));
1550
1551 execute(send_string("get test_ascii_mget1 test_ascii_mget2 test_ascii_mget3 "
1552 "test_ascii_mget4 test_ascii_mget5 "
1553 "test_ascii_mget6\r\n"));
1554 execute(ascii_get_value("test_ascii_mget1", "value"));
1555 execute(ascii_get_value("test_ascii_mget2", "value"));
1556 execute(ascii_get_value("test_ascii_mget3", "value"));
1557 execute(ascii_get_value("test_ascii_mget4", "value"));
1558 execute(ascii_get_value("test_ascii_mget5", "value"));
1559
1560 char buffer[5];
1561 execute(retry_read(buffer, 5));
1562 verify(memcmp(buffer, "END\r\n", 5) == 0);
1563 return TEST_PASS;
1564 }
1565
1566 static enum test_return test_ascii_incr_impl(const char* key, bool noreply)
1567 {
1568 char cmd[300];
1569 sprintf(cmd, "incr %s 1%s\r\n", key, noreply ? " noreply" : "");
1570
1571 execute(ascii_set_item(key, "0"));
1572 for (int x= 1; x < 11; ++x)
1573 {
1574 execute(send_string(cmd));
1575
1576 if (noreply)
1577 execute(test_ascii_version());
1578 else
1579 {
1580 char buffer[80];
1581 execute(receive_line(buffer, sizeof(buffer)));
1582 int val= atoi(buffer);
1583 verify(val == x);
1584 }
1585 }
1586
1587 execute(ascii_get_item(key, "10", true));
1588
1589 return TEST_PASS;
1590 }
1591
1592 static enum test_return test_ascii_incr(void)
1593 {
1594 return test_ascii_incr_impl("test_ascii_incr", false);
1595 }
1596
1597 static enum test_return test_ascii_incr_noreply(void)
1598 {
1599 return test_ascii_incr_impl("test_ascii_incr_noreply", true);
1600 }
1601
1602 static enum test_return test_ascii_decr_impl(const char* key, bool noreply)
1603 {
1604 char cmd[300];
1605 sprintf(cmd, "decr %s 1%s\r\n", key, noreply ? " noreply" : "");
1606
1607 execute(ascii_set_item(key, "9"));
1608 for (int x= 8; x > -1; --x)
1609 {
1610 execute(send_string(cmd));
1611
1612 if (noreply)
1613 execute(test_ascii_version());
1614 else
1615 {
1616 char buffer[80];
1617 execute(receive_line(buffer, sizeof(buffer)));
1618 int val= atoi(buffer);
1619 verify(val == x);
1620 }
1621 }
1622
1623 execute(ascii_get_item(key, "0", true));
1624
1625 /* verify that it doesn't wrap */
1626 execute(send_string(cmd));
1627 if (noreply)
1628 execute(test_ascii_version());
1629 else
1630 {
1631 char buffer[80];
1632 execute(receive_line(buffer, sizeof(buffer)));
1633 }
1634 execute(ascii_get_item(key, "0", true));
1635
1636 return TEST_PASS;
1637 }
1638
1639 static enum test_return test_ascii_decr(void)
1640 {
1641 return test_ascii_decr_impl("test_ascii_decr", false);
1642 }
1643
1644 static enum test_return test_ascii_decr_noreply(void)
1645 {
1646 return test_ascii_decr_impl("test_ascii_decr_noreply", true);
1647 }
1648
1649
1650 static enum test_return test_ascii_flush_impl(const char *key, bool noreply)
1651 {
1652 #if 0
1653 /* Verify that the flush_all command handles unknown options */
1654 /* Bug in the current memcached server! */
1655 execute(send_string("flush_all foo bar\r\n"));
1656 execute(receive_response("ERROR\r\n"));
1657 #endif
1658
1659 execute(ascii_set_item(key, key));
1660 execute(ascii_get_item(key, key, true));
1661
1662 if (noreply)
1663 {
1664 execute(send_string("flush_all noreply\r\n"));
1665 execute(test_ascii_version());
1666 }
1667 else
1668 {
1669 execute(send_string("flush_all\r\n"));
1670 execute(receive_response("OK\r\n"));
1671 }
1672
1673 execute(ascii_get_item(key, key, false));
1674
1675 return TEST_PASS;
1676 }
1677
1678 static enum test_return test_ascii_flush(void)
1679 {
1680 return test_ascii_flush_impl("test_ascii_flush", false);
1681 }
1682
1683 static enum test_return test_ascii_flush_noreply(void)
1684 {
1685 return test_ascii_flush_impl("test_ascii_flush_noreply", true);
1686 }
1687
1688 static enum test_return test_ascii_concat_impl(const char *key,
1689 bool append,
1690 bool noreply)
1691 {
1692 const char *value;
1693
1694 if (append)
1695 value="hello";
1696 else
1697 value=" world";
1698
1699 execute(ascii_set_item(key, value));
1700
1701 if (append)
1702 value=" world";
1703 else
1704 value="hello";
1705
1706 char cmd[400];
1707 sprintf(cmd, "%s %s 0 0 %u%s\r\n%s\r\n",
1708 append ? "append" : "prepend",
1709 key, (unsigned int)strlen(value), noreply ? " noreply" : "",
1710 value);
1711 execute(send_string(cmd));
1712
1713 if (noreply)
1714 execute(test_ascii_version());
1715 else
1716 execute(receive_response("STORED\r\n"));
1717
1718 execute(ascii_get_item(key, "hello world", true));
1719
1720 sprintf(cmd, "%s %s_notfound 0 0 %u%s\r\n%s\r\n",
1721 append ? "append" : "prepend",
1722 key, (unsigned int)strlen(value), noreply ? " noreply" : "",
1723 value);
1724 execute(send_string(cmd));
1725
1726 if (noreply)
1727 execute(test_ascii_version());
1728 else
1729 execute(receive_response("NOT_STORED\r\n"));
1730
1731 return TEST_PASS;
1732 }
1733
1734 static enum test_return test_ascii_append(void)
1735 {
1736 return test_ascii_concat_impl("test_ascii_append", true, false);
1737 }
1738
1739 static enum test_return test_ascii_prepend(void)
1740 {
1741 return test_ascii_concat_impl("test_ascii_prepend", false, false);
1742 }
1743
1744 static enum test_return test_ascii_append_noreply(void)
1745 {
1746 return test_ascii_concat_impl("test_ascii_append_noreply", true, true);
1747 }
1748
1749 static enum test_return test_ascii_prepend_noreply(void)
1750 {
1751 return test_ascii_concat_impl("test_ascii_prepend_noreply", false, true);
1752 }
1753
1754 static enum test_return test_ascii_stat(void)
1755 {
1756 execute(send_string("stats noreply\r\n"));
1757 execute(receive_response("ERROR\r\n"));
1758 execute(send_string("stats\r\n"));
1759 char buffer[1024];
1760 do {
1761 execute(receive_line(buffer, sizeof(buffer)));
1762 } while (strcmp(buffer, "END\r\n") != 0);
1763
1764 return TEST_PASS_RECONNECT;
1765 }
1766
1767 typedef enum test_return(*TEST_FUNC)(void);
1768
1769 struct testcase
1770 {
1771 const char *description;
1772 TEST_FUNC function;
1773 };
1774
1775 struct testcase testcases[]= {
1776 { "ascii quit", test_ascii_quit },
1777 { "ascii version", test_ascii_version },
1778 { "ascii verbosity", test_ascii_verbosity },
1779 { "ascii set", test_ascii_set },
1780 { "ascii set noreply", test_ascii_set_noreply },
1781 { "ascii get", test_ascii_get },
1782 { "ascii gets", test_ascii_gets },
1783 { "ascii mget", test_ascii_mget },
1784 { "ascii flush", test_ascii_flush },
1785 { "ascii flush noreply", test_ascii_flush_noreply },
1786 { "ascii add", test_ascii_add },
1787 { "ascii add noreply", test_ascii_add_noreply },
1788 { "ascii replace", test_ascii_replace },
1789 { "ascii replace noreply", test_ascii_replace_noreply },
1790 { "ascii cas", test_ascii_cas },
1791 { "ascii cas noreply", test_ascii_cas_noreply },
1792 { "ascii delete", test_ascii_delete },
1793 { "ascii delete noreply", test_ascii_delete_noreply },
1794 { "ascii incr", test_ascii_incr },
1795 { "ascii incr noreply", test_ascii_incr_noreply },
1796 { "ascii decr", test_ascii_decr },
1797 { "ascii decr noreply", test_ascii_decr_noreply },
1798 { "ascii append", test_ascii_append },
1799 { "ascii append noreply", test_ascii_append_noreply },
1800 { "ascii prepend", test_ascii_prepend },
1801 { "ascii prepend noreply", test_ascii_prepend_noreply },
1802 { "ascii stat", test_ascii_stat },
1803 { "binary noop", test_binary_noop },
1804 { "binary quit", test_binary_quit },
1805 { "binary quitq", test_binary_quitq },
1806 { "binary set", test_binary_set },
1807 { "binary setq", test_binary_setq },
1808 { "binary flush", test_binary_flush },
1809 { "binary flushq", test_binary_flushq },
1810 { "binary add", test_binary_add },
1811 { "binary addq", test_binary_addq },
1812 { "binary replace", test_binary_replace },
1813 { "binary replaceq", test_binary_replaceq },
1814 { "binary delete", test_binary_delete },
1815 { "binary deleteq", test_binary_deleteq },
1816 { "binary get", test_binary_get },
1817 { "binary getq", test_binary_getq },
1818 { "binary getk", test_binary_getk },
1819 { "binary getkq", test_binary_getkq },
1820 { "binary incr", test_binary_incr },
1821 { "binary incrq", test_binary_incrq },
1822 { "binary decr", test_binary_decr },
1823 { "binary decrq", test_binary_decrq },
1824 { "binary version", test_binary_version },
1825 { "binary append", test_binary_append },
1826 { "binary appendq", test_binary_appendq },
1827 { "binary prepend", test_binary_prepend },
1828 { "binary prependq", test_binary_prependq },
1829 { "binary stat", test_binary_stat },
1830 { "binary illegal", test_binary_illegal },
1831 { NULL, NULL}
1832 };
1833
1834 int main(int argc, char **argv)
1835 {
1836 static const char * const status_msg[]= {"[skip]", "[pass]", "[pass]", "[FAIL]"};
1837 int total= 0;
1838 int failed= 0;
1839 const char *hostname= "localhost";
1840 const char *port= "11211";
1841 int cmd;
1842
1843 while ((cmd= getopt(argc, argv, "t:vch:p:?")) != EOF)
1844 {
1845 switch (cmd) {
1846 case 't':
1847 timeout= atoi(optarg);
1848 if (timeout == 0)
1849 {
1850 fprintf(stderr, "Invalid timeout. Please specify a number for -t\n");
1851 return 1;
1852 }
1853 break;
1854 case 'v': verbose= true;
1855 break;
1856 case 'c': do_core= true;
1857 break;
1858 case 'h': hostname= optarg;
1859 break;
1860 case 'p': port= optarg;
1861 break;
1862 default:
1863 fprintf(stderr, "Usage: %s [-h hostname] [-p port] [-c] [-v] [-t n]\n"
1864 "\t-c\tGenerate coredump if a test fails\n"
1865 "\t-v\tVerbose test output (print out the assertion)\n"
1866 "\t-t n\tSet the timeout for io-operations to n seconds\n",
1867 argv[0]);
1868 return 1;
1869 }
1870 }
1871
1872 sock= connect_server(hostname, port);
1873 if (sock == -1)
1874 {
1875 fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
1876 hostname, port, strerror(errno));
1877 return 1;
1878 }
1879
1880 for (int ii= 0; testcases[ii].description != NULL; ++ii)
1881 {
1882 ++total;
1883 fprintf(stdout, "%-40s", testcases[ii].description);
1884 fflush(stdout);
1885
1886 bool reconnect= false;
1887 enum test_return ret= testcases[ii].function();
1888 if (ret == TEST_FAIL)
1889 {
1890 reconnect= true;
1891 ++failed;
1892 if (verbose)
1893 fprintf(stderr, "\n");
1894 }
1895 else if (ret == TEST_PASS_RECONNECT)
1896 reconnect= true;
1897
1898 fprintf(stderr, "%s\n", status_msg[ret]);
1899 if (reconnect)
1900 {
1901 (void) close(sock);
1902 if ((sock=connect_server(hostname, port)) == -1)
1903 {
1904 fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
1905 hostname, port, strerror(errno));
1906 fprintf(stderr, "%d of %d tests failed\n", failed, total);
1907 return 1;
1908 }
1909 }
1910 }
1911
1912 (void) close(sock);
1913 if (failed == 0)
1914 fprintf(stdout, "All tests passed\n");
1915 else
1916 fprintf(stderr, "%d of %d tests failed\n", failed, total);
1917
1918 return (failed == 0) ? 0 : 1;
1919 }