Merge lp://staging/~trond-norbye/libmemcached/memcapable into lp://staging/~tangent-org/libmemcached/trunk

Proposed by Trond Norbye
Status: Merged
Merged at revision: not available
Proposed branch: lp://staging/~trond-norbye/libmemcached/memcapable
Merge into: lp://staging/~tangent-org/libmemcached/trunk
Diff against target: None lines
To merge this branch: bzr merge lp://staging/~trond-norbye/libmemcached/memcapable
Reviewer Review Type Date Requested Status
Brian Aker Needs Fixing
Review via email: mp+11143@code.staging.launchpad.net
To post a comment you must log in.
Revision history for this message
Trond Norbye (trond-norbye) wrote :

Added new tool memcapable who tries to run the binary protocol on a given server and verifies the output

Revision history for this message
Brian Aker (brianaker) wrote :

I tried to merge this in on gaz but I got many warnings/etc.

review: Approve
Revision history for this message
Brian Aker (brianaker) wrote :

I tried to merge this in on gaz but I got many warnings/etc.

review: Needs Fixing
571. By Brian Aker <brian@gaz>

Merge Trond.

572. By Brian Aker <brian@gaz>

Merging Trond.

573. By Brian Aker <brian@gaz>

Fix for linger behavior

574. By Brian Aker <brian@gaz>

Updating for version .32

575. By Brian Aker <brian@gaz>

Updating library version number

576. By Brian Aker <brian@gaz>

Update pandora

577. By Brian Aker <brian@gaz>

Merge Trond

Updating diff...

An updated diff will be available in a few minutes. Reload to see the changes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'clients/Makefile.am'
--- clients/Makefile.am 2009-07-08 17:58:46 +0000
+++ clients/Makefile.am 2009-09-03 15:09:18 +0000
@@ -1,6 +1,6 @@
1LDADDS = $(top_builddir)/libmemcached/libmemcached.la libutilities.la1LDADDS = $(top_builddir)/libmemcached/libmemcached.la libutilities.la
22
3bin_PROGRAMS = memcat memdump memcp memstat memrm memflush memslap memerror3bin_PROGRAMS = memcat memdump memcp memstat memrm memflush memslap memerror memcapable
44
5noinst_HEADERS = client_options.h \5noinst_HEADERS = client_options.h \
6 utilities.h \6 utilities.h \
@@ -45,6 +45,8 @@
45memslap_LDADD = $(LDADDS) $(PTHREAD_LIBS) libgenexec.la45memslap_LDADD = $(LDADDS) $(PTHREAD_LIBS) libgenexec.la
46memslap_LDFLAGS = $(AM_LDFLAGS) -rpath $(pkglibdir)46memslap_LDFLAGS = $(AM_LDFLAGS) -rpath $(pkglibdir)
4747
48memcapable_SOURCES = memcapable.c
49
48test-start-server:50test-start-server:
49 memflush --servers=localhost51 memflush --servers=localhost
50 memcp --servers=localhost /etc/services52 memcp --servers=localhost /etc/services
5153
=== added file 'clients/memcapable.c'
--- clients/memcapable.c 1970-01-01 00:00:00 +0000
+++ clients/memcapable.c 2009-09-03 15:09:18 +0000
@@ -0,0 +1,1111 @@
1/* -*- Mode: C; tab-width: 3; c-basic-offset: 3; indent-tabs-mode: nil -*- */
2#undef NDEBUG
3#include <pthread.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <netdb.h>
7#include <arpa/inet.h>
8#include <netinet/in.h>
9#include <netinet/tcp.h>
10#include <signal.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <errno.h>
14#include <assert.h>
15#include <string.h>
16#include <inttypes.h>
17#include <stdbool.h>
18#include <unistd.h>
19#include <netinet/in.h>
20
21#include <libmemcached/memcached/protocol_binary.h>
22
23/* Should we generate coredumps when we enounter an error (-c) */
24static bool do_core=false;
25/* connection to the server */
26static int sock;
27
28typedef union
29{
30 protocol_binary_request_no_extras plain;
31 protocol_binary_request_flush flush;
32 protocol_binary_request_incr incr;
33 protocol_binary_request_set set;
34 char bytes[1024];
35} command;
36
37typedef union
38{
39 protocol_binary_response_no_extras plain;
40 protocol_binary_response_incr incr;
41 protocol_binary_response_decr decr;
42 char bytes[1024];
43} response;
44
45enum test_return
46{
47 TEST_SKIP, TEST_PASS, TEST_PASS_RECONNECT, TEST_FAIL
48};
49
50/**
51 * Try to get an addrinfo struct for a given port on a given host
52 */
53static struct addrinfo *lookuphost(const char *hostname, const char *port)
54{
55 struct addrinfo *ai= 0;
56 struct addrinfo hints= {.ai_family=AF_UNSPEC,
57 .ai_protocol=IPPROTO_TCP,
58 .ai_socktype=SOCK_STREAM};
59 int error= getaddrinfo(hostname, port, &hints, &ai);
60
61 if (error != 0)
62 if (error != EAI_SYSTEM)
63 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
64 else
65 perror("getaddrinfo()");
66
67 return ai;
68}
69
70/**
71 * Try to open a connection to the server
72 * @param hostname the name of the server to connect to
73 * @param port the port number (or service) to connect to
74 * @return positive integer if success, -1 otherwise
75 */
76static int connect_server(const char *hostname, const char *port)
77{
78 struct addrinfo *ai= lookuphost(hostname, port);
79 sock= -1;
80 if (ai != NULL)
81 {
82 if ((sock=socket(ai->ai_family, ai->ai_socktype,
83 ai->ai_protocol)) != -1)
84 {
85 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1)
86 {
87 fprintf(stderr, "Failed to connect socket: %s\n",
88 strerror(errno));
89 close(sock);
90 sock= -1;
91 }
92 } else
93 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
94
95 freeaddrinfo(ai);
96 }
97
98 return sock;
99}
100
101/**
102 * Ensure that an expression is true. If it isn't print out a message similar
103 * to assert() and create a coredump if the user wants that. If not an error
104 * message is returned.
105 *
106 */
107static enum test_return ensure(bool val, const char *expression, const char *file, int line)
108{
109 if (!val)
110 {
111 fprintf(stderr, "%s:%u: %s\n", file, line, expression);
112 if (do_core)
113 abort();
114
115 return TEST_FAIL;
116 }
117
118 return TEST_PASS;
119}
120
121#define verify(expression) do { if (ensure(expression, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
122#define execute(expression) do { if (ensure(expression == TEST_PASS, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
123
124/**
125 * Send a chunk of memory over the socket (retry if the call is iterrupted
126 */
127static enum test_return retry_write(const void* buf, size_t len)
128{
129 off_t offset= 0;
130 const char* ptr= buf;
131
132 do
133 {
134 size_t num_bytes= len - offset;
135 ssize_t nw= write(sock, ptr + offset, num_bytes);
136 if (nw == -1)
137 verify(errno == EINTR);
138 else
139 offset+= nw;
140 } while (offset < len);
141
142 return TEST_PASS;
143}
144
145/**
146 * Resend a packet to the server (All fields in the command header should
147 * be in network byte order)
148 */
149static enum test_return resend_packet(command *command)
150{
151 size_t length= sizeof (protocol_binary_request_no_extras) +
152 ntohl(command->plain.message.header.request.bodylen);
153
154 execute(retry_write(command, length));
155 return TEST_PASS;
156}
157
158/**
159 * Send a command to the server. The command header needs to be updated
160 * to network byte order
161 */
162static enum test_return send_packet(command *command)
163{
164 /* Fix the byteorder of the header */
165 command->plain.message.header.request.keylen=
166 ntohs(command->plain.message.header.request.keylen);
167 command->plain.message.header.request.bodylen=
168 ntohl(command->plain.message.header.request.bodylen);
169 command->plain.message.header.request.cas=
170 ntohll(command->plain.message.header.request.cas);
171
172 execute(resend_packet(command));
173 return TEST_PASS;
174}
175
176/**
177 * Read a fixed length chunk of data from the server
178 */
179static enum test_return retry_read(void *buf, size_t len)
180{
181 off_t offset= 0;
182 do
183 {
184 ssize_t nr= read(sock, ((char*) buf) + offset, len - offset);
185 switch (nr) {
186 case -1 :
187 verify(errno == EINTR);
188 break;
189 case 0:
190 return TEST_FAIL;
191 default:
192 offset+= nr;
193 }
194 } while (offset < len);
195
196 return TEST_PASS;
197}
198
199/**
200 * Receive a response from the server and conver the fields in the header
201 * to local byte order
202 */
203static enum test_return recv_packet(response *response)
204{
205 execute(retry_read(response, sizeof (protocol_binary_response_no_extras)));
206
207 /* Fix the byte order in the packet header */
208 response->plain.message.header.response.keylen=
209 ntohs(response->plain.message.header.response.keylen);
210 response->plain.message.header.response.status=
211 ntohs(response->plain.message.header.response.status);
212 response->plain.message.header.response.bodylen=
213 ntohl(response->plain.message.header.response.bodylen);
214 response->plain.message.header.response.cas=
215 ntohll(response->plain.message.header.response.cas);
216
217 size_t bodysz= response->plain.message.header.response.bodylen;
218 if (bodysz > 0)
219 execute(retry_read(response->bytes + sizeof (protocol_binary_response_no_extras), bodysz));
220
221 return TEST_PASS;
222}
223
224/**
225 * Create a storage command (add, set, replace etc)
226 *
227 * @param command destination buffer
228 * @param cmd the storage command to create
229 * @param key the key to store
230 * @param keylen the length of the key
231 * @param dta the data to store with the key
232 * @param dtalen the length of the data to store with the key
233 * @param flags the flags to store along with the key
234 * @param exp the expiry time for the key
235 */
236static void storage_command(command *command,
237 uint8_t cmd,
238 const void* key,
239 size_t keylen,
240 const void* dta,
241 size_t dtalen,
242 uint32_t flags,
243 uint32_t exp)
244{
245 /* all of the storage commands use the same command layout */
246 protocol_binary_request_set *request= &command->set;
247
248 memset(request, 0, sizeof (*request));
249 request->message.header.request.magic= PROTOCOL_BINARY_REQ;
250 request->message.header.request.opcode= cmd;
251 request->message.header.request.keylen= keylen;
252 request->message.header.request.extlen= 8;
253 request->message.header.request.bodylen= keylen + 8 + dtalen;
254 request->message.header.request.opaque= 0xdeadbeef;
255 request->message.body.flags= flags;
256 request->message.body.expiration= exp;
257
258 off_t key_offset= sizeof (protocol_binary_request_no_extras) + 8;
259 memcpy(command->bytes + key_offset, key, keylen);
260 if (dta != NULL)
261 memcpy(command->bytes + key_offset + keylen, dta, dtalen);
262}
263
264/**
265 * Create a basic command to send to the server
266 * @param command destination buffer
267 * @param cmd the command to create
268 * @param key the key to store
269 * @param keylen the length of the key
270 * @param dta the data to store with the key
271 * @param dtalen the length of the data to store with the key
272 */
273static void raw_command(command *command,
274 uint8_t cmd,
275 const void* key,
276 size_t keylen,
277 const void* dta,
278 size_t dtalen)
279{
280 /* all of the storage commands use the same command layout */
281 memset(command, 0, sizeof (*command));
282 command->plain.message.header.request.magic= PROTOCOL_BINARY_REQ;
283 command->plain.message.header.request.opcode= cmd;
284 command->plain.message.header.request.keylen= keylen;
285 command->plain.message.header.request.bodylen= keylen + dtalen;
286 command->plain.message.header.request.opaque= 0xdeadbeef;
287
288 off_t key_offset= sizeof (protocol_binary_request_no_extras);
289
290 if (key != NULL)
291 memcpy(command->bytes + key_offset, key, keylen);
292
293 if (dta != NULL)
294 memcpy(command->bytes + key_offset + keylen, dta, dtalen);
295}
296
297/**
298 * Create the flush command
299 * @param command destination buffer
300 * @param cmd the command to create (FLUSH/FLUSHQ)
301 * @param exptime when to flush
302 * @param use_extra to force using of the extra field?
303 */
304static void flush_command(command *command,
305 uint8_t cmd, uint32_t exptime, bool use_extra)
306{
307 memset(command, 0, sizeof (command->flush));
308 command->flush.message.header.request.magic= PROTOCOL_BINARY_REQ;
309 command->flush.message.header.request.opcode= cmd;
310 command->flush.message.header.request.opaque= 0xdeadbeef;
311
312 if (exptime != 0 || use_extra)
313 {
314 command->flush.message.header.request.extlen= 4;
315 command->flush.message.body.expiration= htonl(exptime);
316 command->flush.message.header.request.bodylen= 4;
317 }
318}
319
320/**
321 * Create a incr/decr command
322 * @param cmd the command to create (FLUSH/FLUSHQ)
323 * @param key the key to operate on
324 * @param keylen the number of bytes in the key
325 * @param delta the number to add/subtract
326 * @param initial the initial value if the key doesn't exist
327 * @param exp when the key should expire if it isn't set
328 */
329static void arithmetic_command(command *command,
330 uint8_t cmd,
331 const void* key,
332 size_t keylen,
333 uint64_t delta,
334 uint64_t initial,
335 uint32_t exp)
336{
337 memset(command, 0, sizeof (command->incr));
338 command->incr.message.header.request.magic= PROTOCOL_BINARY_REQ;
339 command->incr.message.header.request.opcode= cmd;
340 command->incr.message.header.request.keylen= keylen;
341 command->incr.message.header.request.extlen= 20;
342 command->incr.message.header.request.bodylen= keylen + 20;
343 command->incr.message.header.request.opaque= 0xdeadbeef;
344 command->incr.message.body.delta= htonll(delta);
345 command->incr.message.body.initial= htonll(initial);
346 command->incr.message.body.expiration= htonl(exp);
347
348 off_t key_offset= sizeof (protocol_binary_request_no_extras) + 20;
349 memcpy(command->bytes + key_offset, key, keylen);
350}
351
352/**
353 * Validate the response header from the server
354 * @param response the response to check
355 * @param cmd the expected command
356 * @param status the expected status
357 */
358static enum test_return validate_response_header(response *response,
359 uint8_t cmd, uint16_t status)
360{
361 verify(response->plain.message.header.response.magic == PROTOCOL_BINARY_RES);
362 verify(response->plain.message.header.response.opcode == cmd);
363 verify(response->plain.message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
364 verify(response->plain.message.header.response.status == status);
365 verify(response->plain.message.header.response.opaque == 0xdeadbeef);
366
367 if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS)
368 {
369 switch (cmd) {
370 case PROTOCOL_BINARY_CMD_ADDQ:
371 case PROTOCOL_BINARY_CMD_APPENDQ:
372 case PROTOCOL_BINARY_CMD_DECREMENTQ:
373 case PROTOCOL_BINARY_CMD_DELETEQ:
374 case PROTOCOL_BINARY_CMD_FLUSHQ:
375 case PROTOCOL_BINARY_CMD_INCREMENTQ:
376 case PROTOCOL_BINARY_CMD_PREPENDQ:
377 case PROTOCOL_BINARY_CMD_QUITQ:
378 case PROTOCOL_BINARY_CMD_REPLACEQ:
379 case PROTOCOL_BINARY_CMD_SETQ:
380 verify("Quiet command shouldn't return on success" == NULL);
381 default:
382 break;
383 }
384
385 switch (cmd) {
386 case PROTOCOL_BINARY_CMD_ADD:
387 case PROTOCOL_BINARY_CMD_REPLACE:
388 case PROTOCOL_BINARY_CMD_SET:
389 case PROTOCOL_BINARY_CMD_APPEND:
390 case PROTOCOL_BINARY_CMD_PREPEND:
391 verify(response->plain.message.header.response.keylen == 0);
392 verify(response->plain.message.header.response.extlen == 0);
393 verify(response->plain.message.header.response.bodylen == 0);
394 verify(response->plain.message.header.response.cas != 0);
395 break;
396 case PROTOCOL_BINARY_CMD_FLUSH:
397 case PROTOCOL_BINARY_CMD_NOOP:
398 case PROTOCOL_BINARY_CMD_QUIT:
399 case PROTOCOL_BINARY_CMD_DELETE:
400 verify(response->plain.message.header.response.keylen == 0);
401 verify(response->plain.message.header.response.extlen == 0);
402 verify(response->plain.message.header.response.bodylen == 0);
403 verify(response->plain.message.header.response.cas == 0);
404 break;
405
406 case PROTOCOL_BINARY_CMD_DECREMENT:
407 case PROTOCOL_BINARY_CMD_INCREMENT:
408 verify(response->plain.message.header.response.keylen == 0);
409 verify(response->plain.message.header.response.extlen == 0);
410 verify(response->plain.message.header.response.bodylen == 8);
411 verify(response->plain.message.header.response.cas != 0);
412 break;
413
414 case PROTOCOL_BINARY_CMD_STAT:
415 verify(response->plain.message.header.response.extlen == 0);
416 /* key and value exists in all packets except in the terminating */
417 verify(response->plain.message.header.response.cas == 0);
418 break;
419
420 case PROTOCOL_BINARY_CMD_VERSION:
421 verify(response->plain.message.header.response.keylen == 0);
422 verify(response->plain.message.header.response.extlen == 0);
423 verify(response->plain.message.header.response.bodylen != 0);
424 verify(response->plain.message.header.response.cas == 0);
425 break;
426
427 case PROTOCOL_BINARY_CMD_GET:
428 case PROTOCOL_BINARY_CMD_GETQ:
429 verify(response->plain.message.header.response.keylen == 0);
430 verify(response->plain.message.header.response.extlen == 4);
431 verify(response->plain.message.header.response.cas != 0);
432 break;
433
434 case PROTOCOL_BINARY_CMD_GETK:
435 case PROTOCOL_BINARY_CMD_GETKQ:
436 verify(response->plain.message.header.response.keylen != 0);
437 verify(response->plain.message.header.response.extlen == 4);
438 verify(response->plain.message.header.response.cas != 0);
439 break;
440
441 default:
442 /* Undefined command code */
443 break;
444 }
445 }
446 else
447 {
448 verify(response->plain.message.header.response.cas == 0);
449 verify(response->plain.message.header.response.extlen == 0);
450 if (cmd != PROTOCOL_BINARY_CMD_GETK)
451 {
452 verify(response->plain.message.header.response.keylen == 0);
453 }
454 }
455
456 return TEST_PASS;
457}
458
459static enum test_return test_binary_noop(void)
460{
461 command command;
462 response response;
463 raw_command(&command, PROTOCOL_BINARY_CMD_NOOP, NULL, 0, NULL, 0);
464 execute(send_packet(&command));
465 execute(recv_packet(&response));
466 verify(validate_response_header(&response, PROTOCOL_BINARY_CMD_NOOP,
467 PROTOCOL_BINARY_RESPONSE_SUCCESS));
468 return TEST_PASS;
469}
470
471static enum test_return test_binary_quit_impl(uint8_t cmd)
472{
473 command command;
474 response response;
475 raw_command(&command, cmd, NULL, 0, NULL, 0);
476
477 execute(send_packet(&command));
478 if (cmd == PROTOCOL_BINARY_CMD_QUIT)
479 {
480 execute(recv_packet(&response));
481 verify(validate_response_header(&response, PROTOCOL_BINARY_CMD_QUIT,
482 PROTOCOL_BINARY_RESPONSE_SUCCESS));
483 }
484
485 /* Socket should be closed now, read should return 0 */
486 verify(read(sock, response.bytes, sizeof (response.bytes)) == 0);
487
488 return TEST_PASS_RECONNECT;
489}
490
491static enum test_return test_binary_quit(void)
492{
493 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
494}
495
496static enum test_return test_binary_quitq(void)
497{
498 return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
499}
500
501static enum test_return test_binary_set_impl(const char* key, uint8_t cmd)
502{
503 command command;
504 response response;
505
506 uint64_t value= 0xdeadbeefdeadcafe;
507 storage_command(&command, cmd, key, strlen(key), &value, sizeof (value), 0, 0);
508
509 /* set should always work */
510 for (int ii= 0; ii < 10; ii++)
511 {
512 if (ii == 0)
513 execute(send_packet(&command));
514 else
515 execute(resend_packet(&command));
516
517 if (cmd == PROTOCOL_BINARY_CMD_SET)
518 {
519 execute(recv_packet(&response));
520 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
521 }
522 else
523 execute(test_binary_noop());
524 }
525
526 /* try to set with the correct CAS value */
527 command.plain.message.header.request.cas=
528 htonll(response.plain.message.header.response.cas);
529 execute(resend_packet(&command));
530 if (cmd == PROTOCOL_BINARY_CMD_SET)
531 {
532 execute(recv_packet(&response));
533 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
534 }
535 else
536 execute(test_binary_noop());
537
538 /* try to set with an incorrect CAS value */
539 command.plain.message.header.request.cas=
540 htonll(response.plain.message.header.response.cas - 1);
541 execute(resend_packet(&command));
542 execute(recv_packet(&response));
543 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
544
545 return test_binary_noop();
546}
547
548static enum test_return test_binary_set(void)
549{
550 return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
551}
552
553static enum test_return test_binary_setq(void)
554{
555 return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
556}
557
558static enum test_return test_binary_add_impl(const char* key, uint8_t cmd)
559{
560 command command;
561 response response;
562 uint64_t value= 0xdeadbeefdeadcafe;
563 storage_command(&command, cmd, key, strlen(key), &value, sizeof (value), 0, 0);
564
565 /* first add should work, rest of them should fail (even with cas
566 as wildcard */
567 for (int ii=0; ii < 10; ii++)
568 {
569 if (ii == 0)
570 execute(send_packet(&command));
571 else
572 execute(resend_packet(&command));
573
574 if (cmd == PROTOCOL_BINARY_CMD_ADD || ii > 0)
575 {
576 uint16_t expected_result;
577 if (ii == 0)
578 expected_result= PROTOCOL_BINARY_RESPONSE_SUCCESS;
579 else
580 expected_result= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
581
582 execute(recv_packet(&response));
583 verify(validate_response_header(&response, cmd, expected_result));
584 }
585 else
586 execute(test_binary_noop());
587 }
588
589 return TEST_PASS;
590}
591
592static enum test_return test_binary_add(void)
593{
594 return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
595}
596
597static enum test_return test_binary_addq(void)
598{
599 return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
600}
601
602static enum test_return set_item(const char *key, const char *value)
603{
604 command command;
605 response response;
606 storage_command(&command, PROTOCOL_BINARY_CMD_SET, key, strlen(key),
607 value, strlen(value), 0, 0);
608 execute(send_packet(&command));
609 execute(recv_packet(&response));
610 verify(validate_response_header(&response, PROTOCOL_BINARY_CMD_SET,
611 PROTOCOL_BINARY_RESPONSE_SUCCESS));
612 return TEST_PASS;
613}
614
615static enum test_return test_binary_replace_impl(const char* key, uint8_t cmd)
616{
617 command command;
618 response response;
619 uint64_t value= 0xdeadbeefdeadcafe;
620 storage_command(&command, cmd, key, strlen(key), &value, sizeof (value), 0, 0);
621
622 /* first replace should fail, successive should succeed (when the
623 item is added! */
624 for (int ii= 0; ii < 10; ii++)
625 {
626 if (ii == 0)
627 execute(send_packet(&command));
628 else
629 execute(resend_packet(&command));
630
631 if (cmd == PROTOCOL_BINARY_CMD_REPLACE || ii == 0)
632 {
633 uint16_t expected_result;
634 if (ii == 0)
635 expected_result=PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
636 else
637 expected_result=PROTOCOL_BINARY_RESPONSE_SUCCESS;
638
639 execute(recv_packet(&response));
640 verify(validate_response_header(&response, cmd, expected_result));
641
642 if (ii == 0)
643 execute(set_item(key, key));
644 }
645 else
646 execute(test_binary_noop());
647 }
648
649 /* verify that replace with CAS value works! */
650 command.plain.message.header.request.cas=
651 htonll(response.plain.message.header.response.cas);
652 execute(resend_packet(&command));
653
654 if (cmd == PROTOCOL_BINARY_CMD_REPLACE)
655 {
656 execute(recv_packet(&response));
657 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
658 }
659 else
660 execute(test_binary_noop());
661
662 /* try to set with an incorrect CAS value */
663 command.plain.message.header.request.cas=
664 htonll(response.plain.message.header.response.cas - 1);
665 execute(resend_packet(&command));
666 execute(recv_packet(&response));
667 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
668
669 return TEST_PASS;
670}
671
672static enum test_return test_binary_replace(void)
673{
674 return test_binary_replace_impl("test_binary_replace", PROTOCOL_BINARY_CMD_REPLACE);
675}
676
677static enum test_return test_binary_replaceq(void)
678{
679 return test_binary_replace_impl("test_binary_replaceq", PROTOCOL_BINARY_CMD_REPLACEQ);
680}
681
682static enum test_return test_binary_delete_impl(const char *key, uint8_t cmd)
683{
684 command command;
685 response response;
686 raw_command(&command, cmd, key, strlen(key), NULL, 0);
687
688 /* The delete shouldn't work the first time, because the item isn't there */
689 execute(send_packet(&command));
690 execute(recv_packet(&response));
691 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
692 execute(set_item(key, key));
693
694 /* The item should be present now, resend*/
695 execute(resend_packet(&command));
696 if (cmd == PROTOCOL_BINARY_CMD_DELETE)
697 {
698 execute(recv_packet(&response));
699 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
700 }
701
702 execute(test_binary_noop());
703
704 return TEST_PASS;
705}
706
707static enum test_return test_binary_delete(void)
708{
709 return test_binary_delete_impl("test_binary_delete", PROTOCOL_BINARY_CMD_DELETE);
710}
711
712static enum test_return test_binary_deleteq(void)
713{
714 return test_binary_delete_impl("test_binary_deleteq", PROTOCOL_BINARY_CMD_DELETEQ);
715}
716
717static enum test_return test_binary_get_impl(const char *key, uint8_t cmd)
718{
719 command command;
720 response response;
721
722 raw_command(&command, cmd, key, strlen(key), NULL, 0);
723 execute(send_packet(&command));
724
725 if (cmd == PROTOCOL_BINARY_CMD_GET || cmd == PROTOCOL_BINARY_CMD_GETK)
726 {
727 execute(recv_packet(&response));
728 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
729 }
730 else
731 execute(test_binary_noop());
732
733 execute(set_item(key, key));
734 execute(resend_packet(&command));
735 execute(recv_packet(&response));
736 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
737
738 return TEST_PASS;
739}
740
741static enum test_return test_binary_get(void)
742{
743 return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
744}
745
746static enum test_return test_binary_getk(void)
747{
748 return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
749}
750
751static enum test_return test_binary_getq(void)
752{
753 return test_binary_get_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
754}
755
756static enum test_return test_binary_getkq(void)
757{
758 return test_binary_get_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
759}
760
761static enum test_return test_binary_incr_impl(const char* key, uint8_t cmd)
762{
763 command command;
764 response response;
765 arithmetic_command(&command, cmd, key, strlen(key), 1, 0, 0);
766
767 int ii;
768 for (ii= 0; ii < 10; ++ii)
769 {
770 if (ii == 0)
771 execute(send_packet(&command));
772 else
773 execute(resend_packet(&command));
774
775 if (cmd == PROTOCOL_BINARY_CMD_INCREMENT)
776 {
777 execute(recv_packet(&response));
778 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
779 verify(ntohll(response.incr.message.body.value) == ii);
780 }
781 else
782 execute(test_binary_noop());
783 }
784
785 /* @todo add incorrect CAS */
786 return TEST_PASS;
787}
788
789static enum test_return test_binary_incr(void)
790{
791 return test_binary_incr_impl("test_binary_incr", PROTOCOL_BINARY_CMD_INCREMENT);
792}
793
794static enum test_return test_binary_incrq(void)
795{
796 return test_binary_incr_impl("test_binary_incrq", PROTOCOL_BINARY_CMD_INCREMENTQ);
797}
798
799static enum test_return test_binary_decr_impl(const char* key, uint8_t cmd)
800{
801 command command;
802 response response;
803 arithmetic_command(&command, cmd, key, strlen(key), 1, 9, 0);
804
805 int ii;
806 for (ii= 9; ii > -1; --ii)
807 {
808 if (ii == 9)
809 execute(send_packet(&command));
810 else
811 execute(resend_packet(&command));
812
813 if (cmd == PROTOCOL_BINARY_CMD_DECREMENT)
814 {
815 execute(recv_packet(&response));
816 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
817 verify(ntohll(response.decr.message.body.value) == ii);
818 }
819 else
820 execute(test_binary_noop());
821 }
822
823 /* decr 0 should not wrap */
824 execute(resend_packet(&command));
825 if (cmd == PROTOCOL_BINARY_CMD_DECREMENT)
826 {
827 execute(recv_packet(&response));
828 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
829 verify(ntohll(response.decr.message.body.value) == 0);
830 }
831 else
832 {
833 /* @todo get the value and verify! */
834
835 }
836
837 /* @todo add incorrect cas */
838 execute(test_binary_noop());
839 return TEST_PASS;
840}
841
842static enum test_return test_binary_decr(void)
843{
844 return test_binary_decr_impl("test_binary_decr",
845 PROTOCOL_BINARY_CMD_DECREMENT);
846}
847
848static enum test_return test_binary_decrq(void)
849{
850 return test_binary_decr_impl("test_binary_decrq",
851 PROTOCOL_BINARY_CMD_DECREMENTQ);
852}
853
854static enum test_return test_binary_version(void)
855{
856 command command;
857 response response;
858 raw_command(&command, PROTOCOL_BINARY_CMD_VERSION, NULL, 0, NULL, 0);
859
860 execute(send_packet(&command));
861 execute(recv_packet(&response));
862 verify(validate_response_header(&response, PROTOCOL_BINARY_CMD_VERSION,
863 PROTOCOL_BINARY_RESPONSE_SUCCESS));
864
865 return TEST_PASS;
866}
867
868static enum test_return test_binary_flush_impl(const char *key, uint8_t cmd)
869{
870 command command;
871 response response;
872
873 for (int ii= 0; ii < 2; ++ii)
874 {
875 execute(set_item(key, key));
876 flush_command(&command, cmd, 0, ii == 0);
877 execute(send_packet(&command));
878
879 if (cmd == PROTOCOL_BINARY_CMD_FLUSH)
880 {
881 execute(recv_packet(&response));
882 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
883 }
884 else
885 execute(test_binary_noop());
886
887 raw_command(&command, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
888 execute(send_packet(&command));
889 execute(recv_packet(&response));
890 verify(validate_response_header(&response, PROTOCOL_BINARY_CMD_GET,
891 PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
892 }
893
894 return TEST_PASS;
895}
896
897static enum test_return test_binary_flush(void)
898{
899 return test_binary_flush_impl("test_binary_flush", PROTOCOL_BINARY_CMD_FLUSH);
900}
901
902static enum test_return test_binary_flushq(void)
903{
904 return test_binary_flush_impl("test_binary_flushq", PROTOCOL_BINARY_CMD_FLUSHQ);
905}
906
907static enum test_return test_binary_concat_impl(const char *key, uint8_t cmd)
908{
909 command command;
910 response response;
911 const char *value;
912
913 if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_APPENDQ)
914 value="hello";
915 else
916 value=" world";
917
918 execute(set_item(key, value));
919
920 if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_APPENDQ)
921 value=" world";
922 else
923 value="hello";
924
925 raw_command(&command, cmd, key, strlen(key), value, strlen(value));
926 execute(send_packet(&command));
927 if (cmd == PROTOCOL_BINARY_CMD_APPEND || cmd == PROTOCOL_BINARY_CMD_PREPEND)
928 {
929 execute(recv_packet(&response));
930 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS));
931 }
932 else
933 execute(test_binary_noop());
934
935 raw_command(&command, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
936 execute(send_packet(&command));
937 execute(recv_packet(&response));
938 verify(validate_response_header(&response, PROTOCOL_BINARY_CMD_GET,
939 PROTOCOL_BINARY_RESPONSE_SUCCESS));
940 verify(response.plain.message.header.response.bodylen - 4 == 11);
941 verify(memcmp(response.bytes + 28, "hello world", 11) == 0);
942
943 return TEST_PASS;
944}
945
946static enum test_return test_binary_append(void)
947{
948 return test_binary_concat_impl("test_binary_append", PROTOCOL_BINARY_CMD_APPEND);
949}
950
951static enum test_return test_binary_prepend(void)
952{
953 return test_binary_concat_impl("test_binary_prepend", PROTOCOL_BINARY_CMD_PREPEND);
954}
955
956static enum test_return test_binary_appendq(void)
957{
958 return test_binary_concat_impl("test_binary_appendq", PROTOCOL_BINARY_CMD_APPENDQ);
959}
960
961static enum test_return test_binary_prependq(void)
962{
963 return test_binary_concat_impl("test_binary_prependq", PROTOCOL_BINARY_CMD_PREPENDQ);
964}
965
966static enum test_return test_binary_stat(void)
967{
968 command command;
969 response response;
970
971 raw_command(&command, PROTOCOL_BINARY_CMD_STAT, NULL, 0, NULL, 0);
972 execute(send_packet(&command));
973
974 do
975 {
976 execute(recv_packet(&response));
977 verify(validate_response_header(&response, PROTOCOL_BINARY_CMD_STAT,
978 PROTOCOL_BINARY_RESPONSE_SUCCESS));
979 } while (response.plain.message.header.response.keylen != 0);
980
981 return TEST_PASS;
982}
983
984static enum test_return test_binary_illegal(void)
985{
986 command command;
987 response response;
988 uint8_t cmd= 0x1b;
989
990 while (cmd != 0x00)
991 {
992 raw_command(&command, cmd, NULL, 0, NULL, 0);
993 execute(send_packet(&command));
994 execute(recv_packet(&response));
995 verify(validate_response_header(&response, cmd, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND));
996 ++cmd;
997 }
998
999 return TEST_PASS;
1000}
1001
1002typedef enum test_return(*TEST_FUNC)(void);
1003
1004struct testcase
1005{
1006 const char *description;
1007 TEST_FUNC function;
1008};
1009
1010struct testcase testcases[]= {
1011 { "noop", test_binary_noop},
1012 { "quit", test_binary_quit},
1013 { "quitq", test_binary_quitq},
1014 { "set", test_binary_set},
1015 { "setq", test_binary_setq},
1016 { "flush", test_binary_flush},
1017 { "flushq", test_binary_flushq},
1018 { "add", test_binary_add},
1019 { "addq", test_binary_addq},
1020 { "replace", test_binary_replace},
1021 { "replaceq", test_binary_replaceq},
1022 { "delete", test_binary_delete},
1023 { "deleteq", test_binary_deleteq},
1024 { "get", test_binary_get},
1025 { "getq", test_binary_getq},
1026 { "getk", test_binary_getk},
1027 { "getkq", test_binary_getkq},
1028 { "incr", test_binary_incr},
1029 { "incrq", test_binary_incrq},
1030 { "decr", test_binary_decr},
1031 { "decrq", test_binary_decrq},
1032 { "version", test_binary_version},
1033 { "append", test_binary_append},
1034 { "appendq", test_binary_appendq},
1035 { "prepend", test_binary_prepend},
1036 { "prependq", test_binary_prependq},
1037 { "stat", test_binary_stat},
1038 { "illegal", test_binary_illegal},
1039 { NULL, NULL}
1040};
1041
1042int main(int argc, char **argv)
1043{
1044 static const char * const status_msg[]= {"[skip]", "[pass]", "[pass]", "[FAIL]"};
1045 int total= 0;
1046 int failed= 0;
1047 const char *hostname= "localhost";
1048 const char *port= "11211";
1049 int cmd;
1050
1051 while ((cmd= getopt(argc, argv, "ch:p:?")) != EOF)
1052 {
1053 switch (cmd) {
1054 case 'c': do_core= true;
1055 break;
1056 case 'h': hostname= optarg;
1057 break;
1058 case 'p': port= optarg;
1059 break;
1060 default:
1061 fprintf(stderr, "Usage: %s [-h hostname] [-p port]\n", argv[0]);
1062 return 1;
1063 }
1064 }
1065
1066 int sock= connect_server(hostname, port);
1067 if (sock == -1)
1068 {
1069 fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
1070 hostname, port, strerror(errno));
1071 return 1;
1072 }
1073
1074 for (int ii= 0; testcases[ii].description != NULL; ++ii)
1075 {
1076 ++total;
1077 fprintf(stdout, "%s\t\t", testcases[ii].description);
1078 fflush(stdout);
1079
1080 bool reconnect= false;
1081 enum test_return ret= testcases[ii].function();
1082 fprintf(stderr, "%s\n", status_msg[ret]);
1083 if (ret == TEST_FAIL)
1084 {
1085 reconnect= true;
1086 ++failed;
1087 }
1088 else if (ret == TEST_PASS_RECONNECT)
1089 reconnect= true;
1090
1091 if (reconnect)
1092 {
1093 (void) close(sock);
1094 if ((sock=connect_server(hostname, port)) == -1)
1095 {
1096 fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
1097 hostname, port, strerror(errno));
1098 fprintf(stderr, "%d of %d tests failed\n", failed, total);
1099 return 1;
1100 }
1101 }
1102 }
1103
1104 (void) close(sock);
1105 if (failed == 0)
1106 fprintf(stdout, "All tests passed\n");
1107 else
1108 fprintf(stderr, "%d of %d tests failed\n", failed, total);
1109
1110 return (failed == 0) ? 0 : 1;
1111}

Subscribers

People subscribed via source and target branches

to all changes: