Merge lp://staging/~trond-norbye/libmemcached/memcapable into lp://staging/~tangent-org/libmemcached/trunk
- memcapable
- Merge into 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 |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brian Aker | Needs Fixing | ||
Review via email: mp+11143@code.staging.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Trond Norbye (trond-norbye) wrote : | # |
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 | +} |
Added new tool memcapable who tries to run the binary protocol on a given server and verifies the output