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