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

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

Subscribers

People subscribed via source and target branches

to all changes: