~brianaker/libmemcached/1164440

« back to all changes in this revision

Viewing changes to clients/memcapable.cc

Merging bzr://gaz.tangent.org/libmemcached/build/ to Build branch

Show diffs side-by-side

added added

removed removed

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