]>
git.saurik.com Git - redis.git/blob - deps/hiredis/test.c
13 /* The following lines make up our testing "framework" :) */
14 static int tests
= 0, fails
= 0;
15 #define test(_s) { printf("#%02d ", ++tests); printf(_s); }
16 #define test_cond(_c) if(_c) printf("PASSED\n"); else {printf("FAILED\n"); fails++;}
18 static long long usec(void) {
20 gettimeofday(&tv
,NULL
);
21 return (((long long)tv
.tv_sec
)*1000000)+tv
.tv_usec
;
24 static int use_unix
= 0;
25 static redisContext
*blocking_context
= NULL
;
26 static void __connect(redisContext
**target
) {
27 *target
= blocking_context
= (use_unix
?
28 redisConnectUnix("/tmp/redis.sock") : redisConnect((char*)"127.0.0.1", 6379));
29 if (blocking_context
->err
) {
30 printf("Connection error: %s\n", blocking_context
->errstr
);
35 static void test_format_commands(void) {
39 test("Format command without interpolation: ");
40 len
= redisFormatCommand(&cmd
,"SET foo bar");
41 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len
) == 0 &&
42 len
== 4+4+(3+2)+4+(3+2)+4+(3+2));
45 test("Format command with %%s string interpolation: ");
46 len
= redisFormatCommand(&cmd
,"SET %s %s","foo","bar");
47 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len
) == 0 &&
48 len
== 4+4+(3+2)+4+(3+2)+4+(3+2));
51 test("Format command with %%s and an empty string: ");
52 len
= redisFormatCommand(&cmd
,"SET %s %s","foo","");
53 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len
) == 0 &&
54 len
== 4+4+(3+2)+4+(3+2)+4+(0+2));
57 test("Format command with an empty string in between proper interpolations: ");
58 len
= redisFormatCommand(&cmd
,"SET %s %s","","foo");
59 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len
) == 0 &&
60 len
== 4+4+(3+2)+4+(0+2)+4+(3+2));
63 test("Format command with %%b string interpolation: ");
64 len
= redisFormatCommand(&cmd
,"SET %b %b","foo",3,"b\0r",3);
65 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len
) == 0 &&
66 len
== 4+4+(3+2)+4+(3+2)+4+(3+2));
69 test("Format command with %%b and an empty string: ");
70 len
= redisFormatCommand(&cmd
,"SET %b %b","foo",3,"",0);
71 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len
) == 0 &&
72 len
== 4+4+(3+2)+4+(3+2)+4+(0+2));
75 test("Format command with literal %%: ");
76 len
= redisFormatCommand(&cmd
,"SET %% %%");
77 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len
) == 0 &&
78 len
== 4+4+(3+2)+4+(1+2)+4+(1+2));
81 test("Format command with printf-delegation (long long): ");
82 len
= redisFormatCommand(&cmd
,"key:%08lld",1234ll);
83 test_cond(strncmp(cmd
,"*1\r\n$12\r\nkey:00001234\r\n",len
) == 0 &&
87 test("Format command with printf-delegation (float): ");
88 len
= redisFormatCommand(&cmd
,"v:%06.1f",12.34f
);
89 test_cond(strncmp(cmd
,"*1\r\n$8\r\nv:0012.3\r\n",len
) == 0 &&
93 test("Format command with printf-delegation and extra interpolation: ");
94 len
= redisFormatCommand(&cmd
,"key:%d %b",1234,"foo",3);
95 test_cond(strncmp(cmd
,"*2\r\n$8\r\nkey:1234\r\n$3\r\nfoo\r\n",len
) == 0 &&
96 len
== 4+4+(8+2)+4+(3+2));
99 test("Format command with wrong printf format and extra interpolation: ");
100 len
= redisFormatCommand(&cmd
,"key:%08p %b",1234,"foo",3);
101 test_cond(strncmp(cmd
,"*2\r\n$6\r\nkey:8p\r\n$3\r\nfoo\r\n",len
) == 0 &&
102 len
== 4+4+(6+2)+4+(3+2));
107 argv
[1] = "foo\0xxx";
109 size_t lens
[3] = { 3, 7, 3 };
112 test("Format command by passing argc/argv without lengths: ");
113 len
= redisFormatCommandArgv(&cmd
,argc
,argv
,NULL
);
114 test_cond(strncmp(cmd
,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len
) == 0 &&
115 len
== 4+4+(3+2)+4+(3+2)+4+(3+2));
118 test("Format command by passing argc/argv with lengths: ");
119 len
= redisFormatCommandArgv(&cmd
,argc
,argv
,lens
);
120 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 &&
121 len
== 4+4+(3+2)+4+(7+2)+4+(3+2));
125 static void test_blocking_connection(void) {
130 test("Returns error when host cannot be resolved: ");
131 c
= redisConnect((char*)"idontexist.local", 6379);
132 test_cond(c
->err
== REDIS_ERR_OTHER
&&
133 strcmp(c
->errstr
,"Can't resolve: idontexist.local") == 0);
136 test("Returns error when the port is not open: ");
137 c
= redisConnect((char*)"localhost", 56380);
138 test_cond(c
->err
== REDIS_ERR_IO
&&
139 strcmp(c
->errstr
,"Connection refused") == 0);
143 test("Is able to deliver commands: ");
144 reply
= redisCommand(c
,"PING");
145 test_cond(reply
->type
== REDIS_REPLY_STATUS
&&
146 strcasecmp(reply
->str
,"pong") == 0)
147 freeReplyObject(reply
);
149 /* Switch to DB 9 for testing, now that we know we can chat. */
150 reply
= redisCommand(c
,"SELECT 9");
151 freeReplyObject(reply
);
153 /* Make sure the DB is emtpy */
154 reply
= redisCommand(c
,"DBSIZE");
155 if (reply
->type
!= REDIS_REPLY_INTEGER
|| reply
->integer
!= 0) {
156 printf("Database #9 is not empty, test can not continue\n");
159 freeReplyObject(reply
);
161 test("Is a able to send commands verbatim: ");
162 reply
= redisCommand(c
,"SET foo bar");
163 test_cond (reply
->type
== REDIS_REPLY_STATUS
&&
164 strcasecmp(reply
->str
,"ok") == 0)
165 freeReplyObject(reply
);
167 test("%%s String interpolation works: ");
168 reply
= redisCommand(c
,"SET %s %s","foo","hello world");
169 freeReplyObject(reply
);
170 reply
= redisCommand(c
,"GET foo");
171 test_cond(reply
->type
== REDIS_REPLY_STRING
&&
172 strcmp(reply
->str
,"hello world") == 0);
173 freeReplyObject(reply
);
175 test("%%b String interpolation works: ");
176 reply
= redisCommand(c
,"SET %b %b","foo",3,"hello\x00world",11);
177 freeReplyObject(reply
);
178 reply
= redisCommand(c
,"GET foo");
179 test_cond(reply
->type
== REDIS_REPLY_STRING
&&
180 memcmp(reply
->str
,"hello\x00world",11) == 0)
182 test("Binary reply length is correct: ");
183 test_cond(reply
->len
== 11)
184 freeReplyObject(reply
);
186 test("Can parse nil replies: ");
187 reply
= redisCommand(c
,"GET nokey");
188 test_cond(reply
->type
== REDIS_REPLY_NIL
)
189 freeReplyObject(reply
);
192 test("Can parse integer replies: ");
193 reply
= redisCommand(c
,"INCR mycounter");
194 test_cond(reply
->type
== REDIS_REPLY_INTEGER
&& reply
->integer
== 1)
195 freeReplyObject(reply
);
197 test("Can parse multi bulk replies: ");
198 freeReplyObject(redisCommand(c
,"LPUSH mylist foo"));
199 freeReplyObject(redisCommand(c
,"LPUSH mylist bar"));
200 reply
= redisCommand(c
,"LRANGE mylist 0 -1");
201 test_cond(reply
->type
== REDIS_REPLY_ARRAY
&&
202 reply
->elements
== 2 &&
203 !memcmp(reply
->element
[0]->str
,"bar",3) &&
204 !memcmp(reply
->element
[1]->str
,"foo",3))
205 freeReplyObject(reply
);
207 /* m/e with multi bulk reply *before* other reply.
208 * specifically test ordering of reply items to parse. */
209 test("Can handle nested multi bulk replies: ");
210 freeReplyObject(redisCommand(c
,"MULTI"));
211 freeReplyObject(redisCommand(c
,"LRANGE mylist 0 -1"));
212 freeReplyObject(redisCommand(c
,"PING"));
213 reply
= (redisCommand(c
,"EXEC"));
214 test_cond(reply
->type
== REDIS_REPLY_ARRAY
&&
215 reply
->elements
== 2 &&
216 reply
->element
[0]->type
== REDIS_REPLY_ARRAY
&&
217 reply
->element
[0]->elements
== 2 &&
218 !memcmp(reply
->element
[0]->element
[0]->str
,"bar",3) &&
219 !memcmp(reply
->element
[0]->element
[1]->str
,"foo",3) &&
220 reply
->element
[1]->type
== REDIS_REPLY_STATUS
&&
221 strcasecmp(reply
->element
[1]->str
,"pong") == 0);
222 freeReplyObject(reply
);
225 /* Find out Redis version to determine the path for the next test */
226 const char *field
= "redis_version:";
229 reply
= redisCommand(c
,"INFO");
230 p
= strstr(reply
->str
,field
);
231 major
= strtol(p
+strlen(field
),&eptr
,10);
232 p
= eptr
+1; /* char next to the first "." */
233 minor
= strtol(p
,&eptr
,10);
234 freeReplyObject(reply
);
237 test("Returns I/O error when the connection is lost: ");
238 reply
= redisCommand(c
,"QUIT");
239 if (major
>= 2 && minor
> 0) {
240 /* > 2.0 returns OK on QUIT and read() should be issued once more
241 * to know the descriptor is at EOF. */
242 test_cond(strcasecmp(reply
->str
,"OK") == 0 &&
243 redisGetReply(c
,(void**)&reply
) == REDIS_ERR
);
244 freeReplyObject(reply
);
246 test_cond(reply
== NULL
);
249 /* On 2.0, QUIT will cause the connection to be closed immediately and
250 * the read(2) for the reply on QUIT will set the error to EOF.
251 * On >2.0, QUIT will return with OK and another read(2) needed to be
252 * issued to find out the socket was closed by the server. In both
253 * conditions, the error will be set to EOF. */
254 assert(c
->err
== REDIS_ERR_EOF
&&
255 strcmp(c
->errstr
,"Server closed the connection") == 0);
259 test("Returns I/O error on socket timeout: ");
260 struct timeval tv
= { 0, 1000 };
261 assert(redisSetTimeout(c
,tv
) == REDIS_OK
);
262 test_cond(redisGetReply(c
,(void**)&reply
) == REDIS_ERR
&&
263 c
->err
== REDIS_ERR_IO
&& errno
== EAGAIN
);
266 /* Context should be connected */
270 static void test_reply_reader(void) {
276 test("Error handling in reply parser: ");
277 reader
= redisReplyReaderCreate();
278 redisReplyReaderFeed(reader
,(char*)"@foo\r\n",6);
279 ret
= redisReplyReaderGetReply(reader
,NULL
);
280 err
= redisReplyReaderGetError(reader
);
281 test_cond(ret
== REDIS_ERR
&&
282 strcasecmp(err
,"Protocol error, got \"@\" as reply type byte") == 0);
283 redisReplyReaderFree(reader
);
285 /* when the reply already contains multiple items, they must be free'd
286 * on an error. valgrind will bark when this doesn't happen. */
287 test("Memory cleanup in reply parser: ");
288 reader
= redisReplyReaderCreate();
289 redisReplyReaderFeed(reader
,(char*)"*2\r\n",4);
290 redisReplyReaderFeed(reader
,(char*)"$5\r\nhello\r\n",11);
291 redisReplyReaderFeed(reader
,(char*)"@foo\r\n",6);
292 ret
= redisReplyReaderGetReply(reader
,NULL
);
293 err
= redisReplyReaderGetError(reader
);
294 test_cond(ret
== REDIS_ERR
&&
295 strcasecmp(err
,"Protocol error, got \"@\" as reply type byte") == 0);
296 redisReplyReaderFree(reader
);
298 test("Set error on nested multi bulks with depth > 1: ");
299 reader
= redisReplyReaderCreate();
300 redisReplyReaderFeed(reader
,(char*)"*1\r\n",4);
301 redisReplyReaderFeed(reader
,(char*)"*1\r\n",4);
302 redisReplyReaderFeed(reader
,(char*)"*1\r\n",4);
303 ret
= redisReplyReaderGetReply(reader
,NULL
);
304 err
= redisReplyReaderGetError(reader
);
305 test_cond(ret
== REDIS_ERR
&&
306 strncasecmp(err
,"No support for",14) == 0);
307 redisReplyReaderFree(reader
);
309 test("Works with NULL functions for reply: ");
310 reader
= redisReplyReaderCreate();
311 redisReplyReaderSetReplyObjectFunctions(reader
,NULL
);
312 redisReplyReaderFeed(reader
,(char*)"+OK\r\n",5);
313 ret
= redisReplyReaderGetReply(reader
,&reply
);
314 test_cond(ret
== REDIS_OK
&& reply
== (void*)REDIS_REPLY_STATUS
);
315 redisReplyReaderFree(reader
);
317 test("Works when a single newline (\\r\\n) covers two calls to feed: ");
318 reader
= redisReplyReaderCreate();
319 redisReplyReaderSetReplyObjectFunctions(reader
,NULL
);
320 redisReplyReaderFeed(reader
,(char*)"+OK\r",4);
321 ret
= redisReplyReaderGetReply(reader
,&reply
);
322 assert(ret
== REDIS_OK
&& reply
== NULL
);
323 redisReplyReaderFeed(reader
,(char*)"\n",1);
324 ret
= redisReplyReaderGetReply(reader
,&reply
);
325 test_cond(ret
== REDIS_OK
&& reply
== (void*)REDIS_REPLY_STATUS
);
326 redisReplyReaderFree(reader
);
328 test("Properly reset state after protocol error: ");
329 reader
= redisReplyReaderCreate();
330 redisReplyReaderSetReplyObjectFunctions(reader
,NULL
);
331 redisReplyReaderFeed(reader
,(char*)"x",1);
332 ret
= redisReplyReaderGetReply(reader
,&reply
);
333 assert(ret
== REDIS_ERR
);
334 ret
= redisReplyReaderGetReply(reader
,&reply
);
335 test_cond(ret
== REDIS_OK
&& reply
== NULL
)
338 static void test_throughput(void) {
341 redisContext
*c
= blocking_context
;
342 redisReply
**replies
;
344 test("Throughput:\n");
345 for (i
= 0; i
< 500; i
++)
346 freeReplyObject(redisCommand(c
,"LPUSH mylist foo"));
349 replies
= malloc(sizeof(redisReply
*)*num
);
351 for (i
= 0; i
< num
; i
++) {
352 replies
[i
] = redisCommand(c
,"PING");
353 assert(replies
[i
] != NULL
&& replies
[i
]->type
== REDIS_REPLY_STATUS
);
356 for (i
= 0; i
< num
; i
++) freeReplyObject(replies
[i
]);
358 printf("\t(%dx PING: %.3fs)\n", num
, (t2
-t1
)/1000000.0);
360 replies
= malloc(sizeof(redisReply
*)*num
);
362 for (i
= 0; i
< num
; i
++) {
363 replies
[i
] = redisCommand(c
,"LRANGE mylist 0 499");
364 assert(replies
[i
] != NULL
&& replies
[i
]->type
== REDIS_REPLY_ARRAY
);
365 assert(replies
[i
] != NULL
&& replies
[i
]->elements
== 500);
368 for (i
= 0; i
< num
; i
++) freeReplyObject(replies
[i
]);
370 printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num
, (t2
-t1
)/1000000.0);
373 replies
= malloc(sizeof(redisReply
*)*num
);
374 for (i
= 0; i
< num
; i
++)
375 redisAppendCommand(c
,"PING");
377 for (i
= 0; i
< num
; i
++) {
378 assert(redisGetReply(c
, (void*)&replies
[i
]) == REDIS_OK
);
379 assert(replies
[i
] != NULL
&& replies
[i
]->type
== REDIS_REPLY_STATUS
);
382 for (i
= 0; i
< num
; i
++) freeReplyObject(replies
[i
]);
384 printf("\t(%dx PING (pipelined): %.3fs)\n", num
, (t2
-t1
)/1000000.0);
386 replies
= malloc(sizeof(redisReply
*)*num
);
387 for (i
= 0; i
< num
; i
++)
388 redisAppendCommand(c
,"LRANGE mylist 0 499");
390 for (i
= 0; i
< num
; i
++) {
391 assert(redisGetReply(c
, (void*)&replies
[i
]) == REDIS_OK
);
392 assert(replies
[i
] != NULL
&& replies
[i
]->type
== REDIS_REPLY_ARRAY
);
393 assert(replies
[i
] != NULL
&& replies
[i
]->elements
== 500);
396 for (i
= 0; i
< num
; i
++) freeReplyObject(replies
[i
]);
398 printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num
, (t2
-t1
)/1000000.0);
401 static void cleanup(void) {
402 redisContext
*c
= blocking_context
;
405 /* Make sure we're on DB 9 */
406 reply
= redisCommand(c
,"SELECT 9");
407 assert(reply
!= NULL
); freeReplyObject(reply
);
408 reply
= redisCommand(c
,"FLUSHDB");
409 assert(reply
!= NULL
); freeReplyObject(reply
);
413 // static long __test_callback_flags = 0;
414 // static void __test_callback(redisContext *c, void *privdata) {
416 // /* Shift to detect execution order */
417 // __test_callback_flags <<= 8;
418 // __test_callback_flags |= (long)privdata;
421 // static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) {
423 // /* Shift to detect execution order */
424 // __test_callback_flags <<= 8;
425 // __test_callback_flags |= (long)privdata;
426 // if (reply) freeReplyObject(reply);
429 // static redisContext *__connect_nonblock() {
430 // /* Reset callback flags */
431 // __test_callback_flags = 0;
432 // return redisConnectNonBlock("127.0.0.1", 6379, NULL);
435 // static void test_nonblocking_connection() {
439 // test("Calls command callback when command is issued: ");
440 // c = __connect_nonblock();
441 // redisSetCommandCallback(c,__test_callback,(void*)1);
442 // redisCommand(c,"PING");
443 // test_cond(__test_callback_flags == 1);
446 // test("Calls disconnect callback on redisDisconnect: ");
447 // c = __connect_nonblock();
448 // redisSetDisconnectCallback(c,__test_callback,(void*)2);
449 // redisDisconnect(c);
450 // test_cond(__test_callback_flags == 2);
453 // test("Calls disconnect callback and free callback on redisFree: ");
454 // c = __connect_nonblock();
455 // redisSetDisconnectCallback(c,__test_callback,(void*)2);
456 // redisSetFreeCallback(c,__test_callback,(void*)4);
458 // test_cond(__test_callback_flags == ((2 << 8) | 4));
460 // test("redisBufferWrite against empty write buffer: ");
461 // c = __connect_nonblock();
462 // test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);
465 // test("redisBufferWrite against not yet connected fd: ");
466 // c = __connect_nonblock();
467 // redisCommand(c,"PING");
468 // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
469 // strncmp(c->error,"write:",6) == 0);
472 // test("redisBufferWrite against closed fd: ");
473 // c = __connect_nonblock();
474 // redisCommand(c,"PING");
475 // redisDisconnect(c);
476 // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
477 // strncmp(c->error,"write:",6) == 0);
480 // test("Process callbacks in the right sequence: ");
481 // c = __connect_nonblock();
482 // redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING");
483 // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
484 // redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING");
486 // /* Write output buffer */
490 // redisBufferWrite(c,&wdone);
493 // /* Read until at least one callback is executed (the 3 replies will
494 // * arrive in a single packet, causing all callbacks to be executed in
495 // * a single pass). */
496 // while(__test_callback_flags == 0) {
497 // assert(redisBufferRead(c) == REDIS_OK);
498 // redisProcessCallbacks(c);
500 // test_cond(__test_callback_flags == 0x010203);
503 // test("redisDisconnect executes pending callbacks with NULL reply: ");
504 // c = __connect_nonblock();
505 // redisSetDisconnectCallback(c,__test_callback,(void*)1);
506 // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
507 // redisDisconnect(c);
508 // test_cond(__test_callback_flags == 0x0201);
512 int main(int argc
, char **argv
) {
514 if (strcmp(argv
[1],"-s") == 0)
518 signal(SIGPIPE
, SIG_IGN
);
519 test_format_commands();
520 test_blocking_connection();
522 // test_nonblocking_connection();
527 printf("ALL TESTS PASSED\n");
529 printf("*** %d TESTS FAILED ***\n", fails
);