hiredis 0.6.1 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/vendor/hiredis/sds.h CHANGED
@@ -1,6 +1,8 @@
1
- /* SDS (Simple Dynamic Strings), A C dynamic strings library.
1
+ /* SDSLib 2.0 -- A C dynamic strings library
2
2
  *
3
- * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2015, Oran Agra
5
+ * Copyright (c) 2015, Redis Labs, Inc
4
6
  * All rights reserved.
5
7
  *
6
8
  * Redistribution and use in source and binary forms, with or without
@@ -35,32 +37,188 @@
35
37
 
36
38
  #include <sys/types.h>
37
39
  #include <stdarg.h>
40
+ #include <stdint.h>
38
41
 
39
42
  typedef char *sds;
40
43
 
41
- struct sdshdr {
42
- int len;
43
- int free;
44
+ /* Note: sdshdr5 is never used, we just access the flags byte directly.
45
+ * However is here to document the layout of type 5 SDS strings. */
46
+ struct __attribute__ ((__packed__)) sdshdr5 {
47
+ unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
48
+ char buf[];
49
+ };
50
+ struct __attribute__ ((__packed__)) sdshdr8 {
51
+ uint8_t len; /* used */
52
+ uint8_t alloc; /* excluding the header and null terminator */
53
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
54
+ char buf[];
55
+ };
56
+ struct __attribute__ ((__packed__)) sdshdr16 {
57
+ uint16_t len; /* used */
58
+ uint16_t alloc; /* excluding the header and null terminator */
59
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
60
+ char buf[];
61
+ };
62
+ struct __attribute__ ((__packed__)) sdshdr32 {
63
+ uint32_t len; /* used */
64
+ uint32_t alloc; /* excluding the header and null terminator */
65
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
66
+ char buf[];
67
+ };
68
+ struct __attribute__ ((__packed__)) sdshdr64 {
69
+ uint64_t len; /* used */
70
+ uint64_t alloc; /* excluding the header and null terminator */
71
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
44
72
  char buf[];
45
73
  };
46
74
 
75
+ #define SDS_TYPE_5 0
76
+ #define SDS_TYPE_8 1
77
+ #define SDS_TYPE_16 2
78
+ #define SDS_TYPE_32 3
79
+ #define SDS_TYPE_64 4
80
+ #define SDS_TYPE_MASK 7
81
+ #define SDS_TYPE_BITS 3
82
+ #define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
83
+ #define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
84
+ #define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
85
+
47
86
  static inline size_t sdslen(const sds s) {
48
- struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);
49
- return sh->len;
87
+ unsigned char flags = s[-1];
88
+ switch(flags&SDS_TYPE_MASK) {
89
+ case SDS_TYPE_5:
90
+ return SDS_TYPE_5_LEN(flags);
91
+ case SDS_TYPE_8:
92
+ return SDS_HDR(8,s)->len;
93
+ case SDS_TYPE_16:
94
+ return SDS_HDR(16,s)->len;
95
+ case SDS_TYPE_32:
96
+ return SDS_HDR(32,s)->len;
97
+ case SDS_TYPE_64:
98
+ return SDS_HDR(64,s)->len;
99
+ }
100
+ return 0;
50
101
  }
51
102
 
52
103
  static inline size_t sdsavail(const sds s) {
53
- struct sdshdr *sh = (struct sdshdr *)(s-sizeof *sh);
54
- return sh->free;
104
+ unsigned char flags = s[-1];
105
+ switch(flags&SDS_TYPE_MASK) {
106
+ case SDS_TYPE_5: {
107
+ return 0;
108
+ }
109
+ case SDS_TYPE_8: {
110
+ SDS_HDR_VAR(8,s);
111
+ return sh->alloc - sh->len;
112
+ }
113
+ case SDS_TYPE_16: {
114
+ SDS_HDR_VAR(16,s);
115
+ return sh->alloc - sh->len;
116
+ }
117
+ case SDS_TYPE_32: {
118
+ SDS_HDR_VAR(32,s);
119
+ return sh->alloc - sh->len;
120
+ }
121
+ case SDS_TYPE_64: {
122
+ SDS_HDR_VAR(64,s);
123
+ return sh->alloc - sh->len;
124
+ }
125
+ }
126
+ return 0;
127
+ }
128
+
129
+ static inline void sdssetlen(sds s, size_t newlen) {
130
+ unsigned char flags = s[-1];
131
+ switch(flags&SDS_TYPE_MASK) {
132
+ case SDS_TYPE_5:
133
+ {
134
+ unsigned char *fp = ((unsigned char*)s)-1;
135
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
136
+ }
137
+ break;
138
+ case SDS_TYPE_8:
139
+ SDS_HDR(8,s)->len = newlen;
140
+ break;
141
+ case SDS_TYPE_16:
142
+ SDS_HDR(16,s)->len = newlen;
143
+ break;
144
+ case SDS_TYPE_32:
145
+ SDS_HDR(32,s)->len = newlen;
146
+ break;
147
+ case SDS_TYPE_64:
148
+ SDS_HDR(64,s)->len = newlen;
149
+ break;
150
+ }
151
+ }
152
+
153
+ static inline void sdsinclen(sds s, size_t inc) {
154
+ unsigned char flags = s[-1];
155
+ switch(flags&SDS_TYPE_MASK) {
156
+ case SDS_TYPE_5:
157
+ {
158
+ unsigned char *fp = ((unsigned char*)s)-1;
159
+ unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
160
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
161
+ }
162
+ break;
163
+ case SDS_TYPE_8:
164
+ SDS_HDR(8,s)->len += inc;
165
+ break;
166
+ case SDS_TYPE_16:
167
+ SDS_HDR(16,s)->len += inc;
168
+ break;
169
+ case SDS_TYPE_32:
170
+ SDS_HDR(32,s)->len += inc;
171
+ break;
172
+ case SDS_TYPE_64:
173
+ SDS_HDR(64,s)->len += inc;
174
+ break;
175
+ }
176
+ }
177
+
178
+ /* sdsalloc() = sdsavail() + sdslen() */
179
+ static inline size_t sdsalloc(const sds s) {
180
+ unsigned char flags = s[-1];
181
+ switch(flags&SDS_TYPE_MASK) {
182
+ case SDS_TYPE_5:
183
+ return SDS_TYPE_5_LEN(flags);
184
+ case SDS_TYPE_8:
185
+ return SDS_HDR(8,s)->alloc;
186
+ case SDS_TYPE_16:
187
+ return SDS_HDR(16,s)->alloc;
188
+ case SDS_TYPE_32:
189
+ return SDS_HDR(32,s)->alloc;
190
+ case SDS_TYPE_64:
191
+ return SDS_HDR(64,s)->alloc;
192
+ }
193
+ return 0;
194
+ }
195
+
196
+ static inline void sdssetalloc(sds s, size_t newlen) {
197
+ unsigned char flags = s[-1];
198
+ switch(flags&SDS_TYPE_MASK) {
199
+ case SDS_TYPE_5:
200
+ /* Nothing to do, this type has no total allocation info. */
201
+ break;
202
+ case SDS_TYPE_8:
203
+ SDS_HDR(8,s)->alloc = newlen;
204
+ break;
205
+ case SDS_TYPE_16:
206
+ SDS_HDR(16,s)->alloc = newlen;
207
+ break;
208
+ case SDS_TYPE_32:
209
+ SDS_HDR(32,s)->alloc = newlen;
210
+ break;
211
+ case SDS_TYPE_64:
212
+ SDS_HDR(64,s)->alloc = newlen;
213
+ break;
214
+ }
55
215
  }
56
216
 
57
217
  sds sdsnewlen(const void *init, size_t initlen);
58
218
  sds sdsnew(const char *init);
59
219
  sds sdsempty(void);
60
- size_t sdslen(const sds s);
61
220
  sds sdsdup(const sds s);
62
221
  void sdsfree(sds s);
63
- size_t sdsavail(const sds s);
64
222
  sds sdsgrowzero(sds s, size_t len);
65
223
  sds sdscatlen(sds s, const void *t, size_t len);
66
224
  sds sdscat(sds s, const char *t);
@@ -77,7 +235,7 @@ sds sdscatprintf(sds s, const char *fmt, ...);
77
235
  #endif
78
236
 
79
237
  sds sdscatfmt(sds s, char const *fmt, ...);
80
- void sdstrim(sds s, const char *cset);
238
+ sds sdstrim(sds s, const char *cset);
81
239
  void sdsrange(sds s, int start, int end);
82
240
  void sdsupdatelen(sds s);
83
241
  void sdsclear(sds s);
@@ -90,7 +248,7 @@ sds sdsfromlonglong(long long value);
90
248
  sds sdscatrepr(sds s, const char *p, size_t len);
91
249
  sds *sdssplitargs(const char *line, int *argc);
92
250
  sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
93
- sds sdsjoin(char **argv, int argc, char *sep, size_t seplen);
251
+ sds sdsjoin(char **argv, int argc, char *sep);
94
252
  sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
95
253
 
96
254
  /* Low level functions exposed to the user API */
@@ -98,5 +256,18 @@ sds sdsMakeRoomFor(sds s, size_t addlen);
98
256
  void sdsIncrLen(sds s, int incr);
99
257
  sds sdsRemoveFreeSpace(sds s);
100
258
  size_t sdsAllocSize(sds s);
259
+ void *sdsAllocPtr(sds s);
260
+
261
+ /* Export the allocator used by SDS to the program using SDS.
262
+ * Sometimes the program SDS is linked to, may use a different set of
263
+ * allocators, but may want to allocate or free things that SDS will
264
+ * respectively free or allocate. */
265
+ void *sds_malloc(size_t size);
266
+ void *sds_realloc(void *ptr, size_t size);
267
+ void sds_free(void *ptr);
268
+
269
+ #ifdef REDIS_TEST
270
+ int sdsTest(int argc, char *argv[]);
271
+ #endif
101
272
 
102
273
  #endif
@@ -0,0 +1,42 @@
1
+ /* SDSLib 2.0 -- A C dynamic strings library
2
+ *
3
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * Copyright (c) 2015, Oran Agra
5
+ * Copyright (c) 2015, Redis Labs, Inc
6
+ * All rights reserved.
7
+ *
8
+ * Redistribution and use in source and binary forms, with or without
9
+ * modification, are permitted provided that the following conditions are met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright notice,
12
+ * this list of conditions and the following disclaimer.
13
+ * * Redistributions in binary form must reproduce the above copyright
14
+ * notice, this list of conditions and the following disclaimer in the
15
+ * documentation and/or other materials provided with the distribution.
16
+ * * Neither the name of Redis nor the names of its contributors may be used
17
+ * to endorse or promote products derived from this software without
18
+ * specific prior written permission.
19
+ *
20
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ * POSSIBILITY OF SUCH DAMAGE.
31
+ */
32
+
33
+ /* SDS allocator selection.
34
+ *
35
+ * This file is used in order to change the SDS allocator at compile time.
36
+ * Just define the following defines to what you want to use. Also add
37
+ * the include of your alternate allocator if needed (not needed in order
38
+ * to use the default libc allocator). */
39
+
40
+ #define s_malloc malloc
41
+ #define s_realloc realloc
42
+ #define s_free free
@@ -11,6 +11,7 @@
11
11
  #include <limits.h>
12
12
 
13
13
  #include "hiredis.h"
14
+ #include "net.h"
14
15
 
15
16
  enum connection_type {
16
17
  CONN_TCP,
@@ -29,7 +30,7 @@ struct config {
29
30
 
30
31
  struct {
31
32
  const char *path;
32
- } unix;
33
+ } unix_sock;
33
34
  };
34
35
 
35
36
  /* The following lines make up our testing "framework" :) */
@@ -43,6 +44,13 @@ static long long usec(void) {
43
44
  return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
44
45
  }
45
46
 
47
+ /* The assert() calls below have side effects, so we need assert()
48
+ * even if we are compiling without asserts (-DNDEBUG). */
49
+ #ifdef NDEBUG
50
+ #undef assert
51
+ #define assert(e) (void)(e)
52
+ #endif
53
+
46
54
  static redisContext *select_database(redisContext *c) {
47
55
  redisReply *reply;
48
56
 
@@ -89,10 +97,10 @@ static redisContext *connect(struct config config) {
89
97
  if (config.type == CONN_TCP) {
90
98
  c = redisConnect(config.tcp.host, config.tcp.port);
91
99
  } else if (config.type == CONN_UNIX) {
92
- c = redisConnectUnix(config.unix.path);
100
+ c = redisConnectUnix(config.unix_sock.path);
93
101
  } else if (config.type == CONN_FD) {
94
102
  /* Create a dummy connection just to get an fd to inherit */
95
- redisContext *dummy_ctx = redisConnectUnix(config.unix.path);
103
+ redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path);
96
104
  if (dummy_ctx) {
97
105
  int fd = disconnect(dummy_ctx, 1);
98
106
  printf("Connecting to inherited fd %d\n", fd);
@@ -216,6 +224,22 @@ static void test_format_commands(void) {
216
224
  test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
217
225
  len == 4+4+(3+2)+4+(7+2)+4+(3+2));
218
226
  free(cmd);
227
+
228
+ sds sds_cmd;
229
+
230
+ sds_cmd = sdsempty();
231
+ test("Format command into sds by passing argc/argv without lengths: ");
232
+ len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL);
233
+ test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
234
+ len == 4+4+(3+2)+4+(3+2)+4+(3+2));
235
+ sdsfree(sds_cmd);
236
+
237
+ sds_cmd = sdsempty();
238
+ test("Format command into sds by passing argc/argv with lengths: ");
239
+ len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens);
240
+ test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
241
+ len == 4+4+(3+2)+4+(7+2)+4+(3+2));
242
+ sdsfree(sds_cmd);
219
243
  }
220
244
 
221
245
  static void test_append_formatted_commands(struct config config) {
@@ -278,6 +302,82 @@ static void test_reply_reader(void) {
278
302
  strncasecmp(reader->errstr,"No support for",14) == 0);
279
303
  redisReaderFree(reader);
280
304
 
305
+ test("Correctly parses LLONG_MAX: ");
306
+ reader = redisReaderCreate();
307
+ redisReaderFeed(reader, ":9223372036854775807\r\n",22);
308
+ ret = redisReaderGetReply(reader,&reply);
309
+ test_cond(ret == REDIS_OK &&
310
+ ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
311
+ ((redisReply*)reply)->integer == LLONG_MAX);
312
+ freeReplyObject(reply);
313
+ redisReaderFree(reader);
314
+
315
+ test("Set error when > LLONG_MAX: ");
316
+ reader = redisReaderCreate();
317
+ redisReaderFeed(reader, ":9223372036854775808\r\n",22);
318
+ ret = redisReaderGetReply(reader,&reply);
319
+ test_cond(ret == REDIS_ERR &&
320
+ strcasecmp(reader->errstr,"Bad integer value") == 0);
321
+ freeReplyObject(reply);
322
+ redisReaderFree(reader);
323
+
324
+ test("Correctly parses LLONG_MIN: ");
325
+ reader = redisReaderCreate();
326
+ redisReaderFeed(reader, ":-9223372036854775808\r\n",23);
327
+ ret = redisReaderGetReply(reader,&reply);
328
+ test_cond(ret == REDIS_OK &&
329
+ ((redisReply*)reply)->type == REDIS_REPLY_INTEGER &&
330
+ ((redisReply*)reply)->integer == LLONG_MIN);
331
+ freeReplyObject(reply);
332
+ redisReaderFree(reader);
333
+
334
+ test("Set error when < LLONG_MIN: ");
335
+ reader = redisReaderCreate();
336
+ redisReaderFeed(reader, ":-9223372036854775809\r\n",23);
337
+ ret = redisReaderGetReply(reader,&reply);
338
+ test_cond(ret == REDIS_ERR &&
339
+ strcasecmp(reader->errstr,"Bad integer value") == 0);
340
+ freeReplyObject(reply);
341
+ redisReaderFree(reader);
342
+
343
+ test("Set error when array < -1: ");
344
+ reader = redisReaderCreate();
345
+ redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12);
346
+ ret = redisReaderGetReply(reader,&reply);
347
+ test_cond(ret == REDIS_ERR &&
348
+ strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
349
+ freeReplyObject(reply);
350
+ redisReaderFree(reader);
351
+
352
+ test("Set error when bulk < -1: ");
353
+ reader = redisReaderCreate();
354
+ redisReaderFeed(reader, "$-2\r\nasdf\r\n",11);
355
+ ret = redisReaderGetReply(reader,&reply);
356
+ test_cond(ret == REDIS_ERR &&
357
+ strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
358
+ freeReplyObject(reply);
359
+ redisReaderFree(reader);
360
+
361
+ test("Set error when array > INT_MAX: ");
362
+ reader = redisReaderCreate();
363
+ redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);
364
+ ret = redisReaderGetReply(reader,&reply);
365
+ test_cond(ret == REDIS_ERR &&
366
+ strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0);
367
+ freeReplyObject(reply);
368
+ redisReaderFree(reader);
369
+
370
+ #if LLONG_MAX > SIZE_MAX
371
+ test("Set error when bulk > SIZE_MAX: ");
372
+ reader = redisReaderCreate();
373
+ redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28);
374
+ ret = redisReaderGetReply(reader,&reply);
375
+ test_cond(ret == REDIS_ERR &&
376
+ strcasecmp(reader->errstr,"Bulk string length out of range") == 0);
377
+ freeReplyObject(reply);
378
+ redisReaderFree(reader);
379
+ #endif
380
+
281
381
  test("Works with NULL functions for reply: ");
282
382
  reader = redisReaderCreate();
283
383
  reader->fn = NULL;
@@ -320,12 +420,12 @@ static void test_reply_reader(void) {
320
420
  }
321
421
 
322
422
  static void test_free_null(void) {
323
- void *redisContext = NULL;
423
+ void *redisCtx = NULL;
324
424
  void *reply = NULL;
325
425
 
326
426
  test("Don't fail when redisFree is passed a NULL value: ");
327
- redisFree(redisContext);
328
- test_cond(redisContext == NULL);
427
+ redisFree(redisCtx);
428
+ test_cond(redisCtx == NULL);
329
429
 
330
430
  test("Don't fail when freeReplyObject is passed a NULL value: ");
331
431
  freeReplyObject(reply);
@@ -343,6 +443,7 @@ static void test_blocking_connection_errors(void) {
343
443
  strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 ||
344
444
  strcmp(c->errstr,"No address associated with hostname") == 0 ||
345
445
  strcmp(c->errstr,"Temporary failure in name resolution") == 0 ||
446
+ strcmp(c->errstr,"hostname nor servname provided, or not known") == 0 ||
346
447
  strcmp(c->errstr,"no address associated with name") == 0));
347
448
  redisFree(c);
348
449
 
@@ -352,7 +453,7 @@ static void test_blocking_connection_errors(void) {
352
453
  strcmp(c->errstr,"Connection refused") == 0);
353
454
  redisFree(c);
354
455
 
355
- test("Returns error when the unix socket path doesn't accept connections: ");
456
+ test("Returns error when the unix_sock socket path doesn't accept connections: ");
356
457
  c = redisConnectUnix((char*)"/tmp/idontexist.sock");
357
458
  test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
358
459
  redisFree(c);
@@ -436,6 +537,52 @@ static void test_blocking_connection(struct config config) {
436
537
  disconnect(c, 0);
437
538
  }
438
539
 
540
+ static void test_blocking_connection_timeouts(struct config config) {
541
+ redisContext *c;
542
+ redisReply *reply;
543
+ ssize_t s;
544
+ const char *cmd = "DEBUG SLEEP 3\r\n";
545
+ struct timeval tv;
546
+
547
+ c = connect(config);
548
+ test("Successfully completes a command when the timeout is not exceeded: ");
549
+ reply = redisCommand(c,"SET foo fast");
550
+ freeReplyObject(reply);
551
+ tv.tv_sec = 0;
552
+ tv.tv_usec = 10000;
553
+ redisSetTimeout(c, tv);
554
+ reply = redisCommand(c, "GET foo");
555
+ test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0);
556
+ freeReplyObject(reply);
557
+ disconnect(c, 0);
558
+
559
+ c = connect(config);
560
+ test("Does not return a reply when the command times out: ");
561
+ s = write(c->fd, cmd, strlen(cmd));
562
+ tv.tv_sec = 0;
563
+ tv.tv_usec = 10000;
564
+ redisSetTimeout(c, tv);
565
+ reply = redisCommand(c, "GET foo");
566
+ test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0);
567
+ freeReplyObject(reply);
568
+
569
+ test("Reconnect properly reconnects after a timeout: ");
570
+ redisReconnect(c);
571
+ reply = redisCommand(c, "PING");
572
+ test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
573
+ freeReplyObject(reply);
574
+
575
+ test("Reconnect properly uses owned parameters: ");
576
+ config.tcp.host = "foo";
577
+ config.unix_sock.path = "foo";
578
+ redisReconnect(c);
579
+ reply = redisCommand(c, "PING");
580
+ test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
581
+ freeReplyObject(reply);
582
+
583
+ disconnect(c, 0);
584
+ }
585
+
439
586
  static void test_blocking_io_errors(struct config config) {
440
587
  redisContext *c;
441
588
  redisReply *reply;
@@ -459,7 +606,7 @@ static void test_blocking_io_errors(struct config config) {
459
606
 
460
607
  test("Returns I/O error when the connection is lost: ");
461
608
  reply = redisCommand(c,"QUIT");
462
- if (major >= 2 && minor > 0) {
609
+ if (major > 2 || (major == 2 && minor > 0)) {
463
610
  /* > 2.0 returns OK on QUIT and read() should be issued once more
464
611
  * to know the descriptor is at EOF. */
465
612
  test_cond(strcasecmp(reply->str,"OK") == 0 &&
@@ -497,7 +644,7 @@ static void test_invalid_timeout_errors(struct config config) {
497
644
 
498
645
  c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
499
646
 
500
- test_cond(c->err == REDIS_ERR_IO);
647
+ test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
501
648
  redisFree(c);
502
649
 
503
650
  test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: ");
@@ -507,7 +654,7 @@ static void test_invalid_timeout_errors(struct config config) {
507
654
 
508
655
  c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout);
509
656
 
510
- test_cond(c->err == REDIS_ERR_IO);
657
+ test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
511
658
  redisFree(c);
512
659
  }
513
660
 
@@ -545,6 +692,17 @@ static void test_throughput(struct config config) {
545
692
  free(replies);
546
693
  printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
547
694
 
695
+ replies = malloc(sizeof(redisReply*)*num);
696
+ t1 = usec();
697
+ for (i = 0; i < num; i++) {
698
+ replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000);
699
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
700
+ }
701
+ t2 = usec();
702
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
703
+ free(replies);
704
+ printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0);
705
+
548
706
  num = 10000;
549
707
  replies = malloc(sizeof(redisReply*)*num);
550
708
  for (i = 0; i < num; i++)
@@ -573,6 +731,19 @@ static void test_throughput(struct config config) {
573
731
  free(replies);
574
732
  printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
575
733
 
734
+ replies = malloc(sizeof(redisReply*)*num);
735
+ for (i = 0; i < num; i++)
736
+ redisAppendCommand(c,"INCRBY incrkey %d", 1000000);
737
+ t1 = usec();
738
+ for (i = 0; i < num; i++) {
739
+ assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
740
+ assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER);
741
+ }
742
+ t2 = usec();
743
+ for (i = 0; i < num; i++) freeReplyObject(replies[i]);
744
+ free(replies);
745
+ printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
746
+
576
747
  disconnect(c, 0);
577
748
  }
578
749
 
@@ -681,7 +852,7 @@ int main(int argc, char **argv) {
681
852
  .host = "127.0.0.1",
682
853
  .port = 6379
683
854
  },
684
- .unix = {
855
+ .unix_sock = {
685
856
  .path = "/tmp/redis.sock"
686
857
  }
687
858
  };
@@ -702,7 +873,7 @@ int main(int argc, char **argv) {
702
873
  cfg.tcp.port = atoi(argv[0]);
703
874
  } else if (argc >= 2 && !strcmp(argv[0],"-s")) {
704
875
  argv++; argc--;
705
- cfg.unix.path = argv[0];
876
+ cfg.unix_sock.path = argv[0];
706
877
  } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
707
878
  throughput = 0;
708
879
  } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
@@ -722,19 +893,21 @@ int main(int argc, char **argv) {
722
893
  printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
723
894
  cfg.type = CONN_TCP;
724
895
  test_blocking_connection(cfg);
896
+ test_blocking_connection_timeouts(cfg);
725
897
  test_blocking_io_errors(cfg);
726
898
  test_invalid_timeout_errors(cfg);
727
899
  test_append_formatted_commands(cfg);
728
900
  if (throughput) test_throughput(cfg);
729
901
 
730
- printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
902
+ printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path);
731
903
  cfg.type = CONN_UNIX;
732
904
  test_blocking_connection(cfg);
905
+ test_blocking_connection_timeouts(cfg);
733
906
  test_blocking_io_errors(cfg);
734
907
  if (throughput) test_throughput(cfg);
735
908
 
736
909
  if (test_inherit_fd) {
737
- printf("\nTesting against inherited fd (%s):\n", cfg.unix.path);
910
+ printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path);
738
911
  cfg.type = CONN_FD;
739
912
  test_blocking_connection(cfg);
740
913
  }