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