]>
Commit | Line | Data |
---|---|---|
1 | #include <stdio.h> | |
2 | #include <stdlib.h> | |
3 | #include <string.h> | |
4 | #include <strings.h> | |
5 | #include <sys/time.h> | |
6 | #include <assert.h> | |
7 | #include <unistd.h> | |
8 | #include <signal.h> | |
9 | ||
10 | #include "hiredis.h" | |
11 | ||
12 | /* The following lines make up our testing "framework" :) */ | |
13 | static int tests = 0, fails = 0; | |
14 | #define test(_s) { printf("#%02d ", ++tests); printf(_s); } | |
15 | #define test_cond(_c) if(_c) printf("PASSED\n"); else {printf("FAILED\n"); fails++;} | |
16 | ||
17 | static long long usec(void) { | |
18 | struct timeval tv; | |
19 | gettimeofday(&tv,NULL); | |
20 | return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; | |
21 | } | |
22 | ||
23 | static int use_unix = 0; | |
24 | static redisContext *blocking_context = NULL; | |
25 | static void __connect(redisContext **target) { | |
26 | *target = blocking_context = (use_unix ? | |
27 | redisConnectUnix("/tmp/redis.sock") : redisConnect((char*)"127.0.0.1", 6379)); | |
28 | if (blocking_context->err) { | |
29 | printf("Connection error: %s\n", blocking_context->errstr); | |
30 | exit(1); | |
31 | } | |
32 | } | |
33 | ||
34 | static void test_format_commands() { | |
35 | char *cmd; | |
36 | int len; | |
37 | ||
38 | test("Format command without interpolation: "); | |
39 | len = redisFormatCommand(&cmd,"SET foo bar"); | |
40 | test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && | |
41 | len == 4+4+(3+2)+4+(3+2)+4+(3+2)); | |
42 | free(cmd); | |
43 | ||
44 | test("Format command with %%s string interpolation: "); | |
45 | len = redisFormatCommand(&cmd,"SET %s %s","foo","bar"); | |
46 | test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && | |
47 | len == 4+4+(3+2)+4+(3+2)+4+(3+2)); | |
48 | free(cmd); | |
49 | ||
50 | test("Format command with %%s and an empty string: "); | |
51 | len = redisFormatCommand(&cmd,"SET %s %s","foo",""); | |
52 | test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && | |
53 | len == 4+4+(3+2)+4+(3+2)+4+(0+2)); | |
54 | free(cmd); | |
55 | ||
56 | test("Format command with %%b string interpolation: "); | |
57 | len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3); | |
58 | 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 && | |
59 | len == 4+4+(3+2)+4+(3+2)+4+(3+2)); | |
60 | free(cmd); | |
61 | ||
62 | test("Format command with %%b and an empty string: "); | |
63 | len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0); | |
64 | test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && | |
65 | len == 4+4+(3+2)+4+(3+2)+4+(0+2)); | |
66 | free(cmd); | |
67 | ||
68 | test("Format command with literal %%: "); | |
69 | len = redisFormatCommand(&cmd,"SET %% %%"); | |
70 | test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 && | |
71 | len == 4+4+(3+2)+4+(1+2)+4+(1+2)); | |
72 | free(cmd); | |
73 | ||
74 | test("Format command with printf-delegation (long long): "); | |
75 | len = redisFormatCommand(&cmd,"key:%08lld",1234ll); | |
76 | test_cond(strncmp(cmd,"*1\r\n$12\r\nkey:00001234\r\n",len) == 0 && | |
77 | len == 4+5+(12+2)); | |
78 | free(cmd); | |
79 | ||
80 | test("Format command with printf-delegation (float): "); | |
81 | len = redisFormatCommand(&cmd,"v:%06.1f",12.34f); | |
82 | test_cond(strncmp(cmd,"*1\r\n$8\r\nv:0012.3\r\n",len) == 0 && | |
83 | len == 4+4+(8+2)); | |
84 | free(cmd); | |
85 | ||
86 | test("Format command with printf-delegation and extra interpolation: "); | |
87 | len = redisFormatCommand(&cmd,"key:%d %b",1234,"foo",3); | |
88 | test_cond(strncmp(cmd,"*2\r\n$8\r\nkey:1234\r\n$3\r\nfoo\r\n",len) == 0 && | |
89 | len == 4+4+(8+2)+4+(3+2)); | |
90 | free(cmd); | |
91 | ||
92 | test("Format command with wrong printf format and extra interpolation: "); | |
93 | len = redisFormatCommand(&cmd,"key:%08p %b",1234,"foo",3); | |
94 | test_cond(strncmp(cmd,"*2\r\n$6\r\nkey:8p\r\n$3\r\nfoo\r\n",len) == 0 && | |
95 | len == 4+4+(6+2)+4+(3+2)); | |
96 | free(cmd); | |
97 | ||
98 | const char *argv[3]; | |
99 | argv[0] = "SET"; | |
100 | argv[1] = "foo\0xxx"; | |
101 | argv[2] = "bar"; | |
102 | size_t lens[3] = { 3, 7, 3 }; | |
103 | int argc = 3; | |
104 | ||
105 | test("Format command by passing argc/argv without lengths: "); | |
106 | len = redisFormatCommandArgv(&cmd,argc,argv,NULL); | |
107 | test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && | |
108 | len == 4+4+(3+2)+4+(3+2)+4+(3+2)); | |
109 | free(cmd); | |
110 | ||
111 | test("Format command by passing argc/argv with lengths: "); | |
112 | len = redisFormatCommandArgv(&cmd,argc,argv,lens); | |
113 | 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 && | |
114 | len == 4+4+(3+2)+4+(7+2)+4+(3+2)); | |
115 | free(cmd); | |
116 | } | |
117 | ||
118 | static void test_blocking_connection() { | |
119 | redisContext *c; | |
120 | redisReply *reply; | |
121 | int major, minor; | |
122 | ||
123 | test("Returns error when host cannot be resolved: "); | |
124 | c = redisConnect((char*)"idontexist.local", 6379); | |
125 | test_cond(c->err == REDIS_ERR_OTHER && | |
126 | strcmp(c->errstr,"Can't resolve: idontexist.local") == 0); | |
127 | redisFree(c); | |
128 | ||
129 | test("Returns error when the port is not open: "); | |
130 | c = redisConnect((char*)"localhost", 56380); | |
131 | test_cond(c->err == REDIS_ERR_IO && | |
132 | strcmp(c->errstr,"Connection refused") == 0); | |
133 | redisFree(c); | |
134 | ||
135 | __connect(&c); | |
136 | test("Is able to deliver commands: "); | |
137 | reply = redisCommand(c,"PING"); | |
138 | test_cond(reply->type == REDIS_REPLY_STATUS && | |
139 | strcasecmp(reply->str,"pong") == 0) | |
140 | freeReplyObject(reply); | |
141 | ||
142 | /* Switch to DB 9 for testing, now that we know we can chat. */ | |
143 | reply = redisCommand(c,"SELECT 9"); | |
144 | freeReplyObject(reply); | |
145 | ||
146 | /* Make sure the DB is emtpy */ | |
147 | reply = redisCommand(c,"DBSIZE"); | |
148 | if (reply->type != REDIS_REPLY_INTEGER || reply->integer != 0) { | |
149 | printf("Database #9 is not empty, test can not continue\n"); | |
150 | exit(1); | |
151 | } | |
152 | freeReplyObject(reply); | |
153 | ||
154 | test("Is a able to send commands verbatim: "); | |
155 | reply = redisCommand(c,"SET foo bar"); | |
156 | test_cond (reply->type == REDIS_REPLY_STATUS && | |
157 | strcasecmp(reply->str,"ok") == 0) | |
158 | freeReplyObject(reply); | |
159 | ||
160 | test("%%s String interpolation works: "); | |
161 | reply = redisCommand(c,"SET %s %s","foo","hello world"); | |
162 | freeReplyObject(reply); | |
163 | reply = redisCommand(c,"GET foo"); | |
164 | test_cond(reply->type == REDIS_REPLY_STRING && | |
165 | strcmp(reply->str,"hello world") == 0); | |
166 | freeReplyObject(reply); | |
167 | ||
168 | test("%%b String interpolation works: "); | |
169 | reply = redisCommand(c,"SET %b %b","foo",3,"hello\x00world",11); | |
170 | freeReplyObject(reply); | |
171 | reply = redisCommand(c,"GET foo"); | |
172 | test_cond(reply->type == REDIS_REPLY_STRING && | |
173 | memcmp(reply->str,"hello\x00world",11) == 0) | |
174 | ||
175 | test("Binary reply length is correct: "); | |
176 | test_cond(reply->len == 11) | |
177 | freeReplyObject(reply); | |
178 | ||
179 | test("Can parse nil replies: "); | |
180 | reply = redisCommand(c,"GET nokey"); | |
181 | test_cond(reply->type == REDIS_REPLY_NIL) | |
182 | freeReplyObject(reply); | |
183 | ||
184 | /* test 7 */ | |
185 | test("Can parse integer replies: "); | |
186 | reply = redisCommand(c,"INCR mycounter"); | |
187 | test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) | |
188 | freeReplyObject(reply); | |
189 | ||
190 | test("Can parse multi bulk replies: "); | |
191 | freeReplyObject(redisCommand(c,"LPUSH mylist foo")); | |
192 | freeReplyObject(redisCommand(c,"LPUSH mylist bar")); | |
193 | reply = redisCommand(c,"LRANGE mylist 0 -1"); | |
194 | test_cond(reply->type == REDIS_REPLY_ARRAY && | |
195 | reply->elements == 2 && | |
196 | !memcmp(reply->element[0]->str,"bar",3) && | |
197 | !memcmp(reply->element[1]->str,"foo",3)) | |
198 | freeReplyObject(reply); | |
199 | ||
200 | /* m/e with multi bulk reply *before* other reply. | |
201 | * specifically test ordering of reply items to parse. */ | |
202 | test("Can handle nested multi bulk replies: "); | |
203 | freeReplyObject(redisCommand(c,"MULTI")); | |
204 | freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1")); | |
205 | freeReplyObject(redisCommand(c,"PING")); | |
206 | reply = (redisCommand(c,"EXEC")); | |
207 | test_cond(reply->type == REDIS_REPLY_ARRAY && | |
208 | reply->elements == 2 && | |
209 | reply->element[0]->type == REDIS_REPLY_ARRAY && | |
210 | reply->element[0]->elements == 2 && | |
211 | !memcmp(reply->element[0]->element[0]->str,"bar",3) && | |
212 | !memcmp(reply->element[0]->element[1]->str,"foo",3) && | |
213 | reply->element[1]->type == REDIS_REPLY_STATUS && | |
214 | strcasecmp(reply->element[1]->str,"pong") == 0); | |
215 | freeReplyObject(reply); | |
216 | ||
217 | { | |
218 | /* Find out Redis version to determine the path for the next test */ | |
219 | const char *field = "redis_version:"; | |
220 | char *p, *eptr; | |
221 | ||
222 | reply = redisCommand(c,"INFO"); | |
223 | p = strstr(reply->str,field); | |
224 | major = strtol(p+strlen(field),&eptr,10); | |
225 | p = eptr+1; /* char next to the first "." */ | |
226 | minor = strtol(p,&eptr,10); | |
227 | freeReplyObject(reply); | |
228 | } | |
229 | ||
230 | test("Returns I/O error when the connection is lost: "); | |
231 | reply = redisCommand(c,"QUIT"); | |
232 | if (major >= 2 && minor > 0) { | |
233 | /* > 2.0 returns OK on QUIT and read() should be issued once more | |
234 | * to know the descriptor is at EOF. */ | |
235 | test_cond(strcasecmp(reply->str,"OK") == 0 && | |
236 | redisGetReply(c,(void**)&reply) == REDIS_ERR); | |
237 | freeReplyObject(reply); | |
238 | } else { | |
239 | test_cond(reply == NULL); | |
240 | } | |
241 | ||
242 | /* On 2.0, QUIT will cause the connection to be closed immediately and | |
243 | * the read(2) for the reply on QUIT will set the error to EOF. | |
244 | * On >2.0, QUIT will return with OK and another read(2) needed to be | |
245 | * issued to find out the socket was closed by the server. In both | |
246 | * conditions, the error will be set to EOF. */ | |
247 | assert(c->err == REDIS_ERR_EOF && | |
248 | strcmp(c->errstr,"Server closed the connection") == 0); | |
249 | ||
250 | /* Clean up context and reconnect again */ | |
251 | redisFree(c); | |
252 | __connect(&c); | |
253 | } | |
254 | ||
255 | static void test_reply_reader() { | |
256 | void *reader; | |
257 | void *reply; | |
258 | char *err; | |
259 | int ret; | |
260 | ||
261 | test("Error handling in reply parser: "); | |
262 | reader = redisReplyReaderCreate(); | |
263 | redisReplyReaderFeed(reader,(char*)"@foo\r\n",6); | |
264 | ret = redisReplyReaderGetReply(reader,NULL); | |
265 | err = redisReplyReaderGetError(reader); | |
266 | test_cond(ret == REDIS_ERR && | |
267 | strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0); | |
268 | redisReplyReaderFree(reader); | |
269 | ||
270 | /* when the reply already contains multiple items, they must be free'd | |
271 | * on an error. valgrind will bark when this doesn't happen. */ | |
272 | test("Memory cleanup in reply parser: "); | |
273 | reader = redisReplyReaderCreate(); | |
274 | redisReplyReaderFeed(reader,(char*)"*2\r\n",4); | |
275 | redisReplyReaderFeed(reader,(char*)"$5\r\nhello\r\n",11); | |
276 | redisReplyReaderFeed(reader,(char*)"@foo\r\n",6); | |
277 | ret = redisReplyReaderGetReply(reader,NULL); | |
278 | err = redisReplyReaderGetError(reader); | |
279 | test_cond(ret == REDIS_ERR && | |
280 | strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0); | |
281 | redisReplyReaderFree(reader); | |
282 | ||
283 | test("Set error on nested multi bulks with depth > 1: "); | |
284 | reader = redisReplyReaderCreate(); | |
285 | redisReplyReaderFeed(reader,(char*)"*1\r\n",4); | |
286 | redisReplyReaderFeed(reader,(char*)"*1\r\n",4); | |
287 | redisReplyReaderFeed(reader,(char*)"*1\r\n",4); | |
288 | ret = redisReplyReaderGetReply(reader,NULL); | |
289 | err = redisReplyReaderGetError(reader); | |
290 | test_cond(ret == REDIS_ERR && | |
291 | strncasecmp(err,"No support for",14) == 0); | |
292 | redisReplyReaderFree(reader); | |
293 | ||
294 | test("Works with NULL functions for reply: "); | |
295 | reader = redisReplyReaderCreate(); | |
296 | redisReplyReaderSetReplyObjectFunctions(reader,NULL); | |
297 | redisReplyReaderFeed(reader,(char*)"+OK\r\n",5); | |
298 | ret = redisReplyReaderGetReply(reader,&reply); | |
299 | test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); | |
300 | redisReplyReaderFree(reader); | |
301 | ||
302 | test("Works when a single newline (\\r\\n) covers two calls to feed: "); | |
303 | reader = redisReplyReaderCreate(); | |
304 | redisReplyReaderSetReplyObjectFunctions(reader,NULL); | |
305 | redisReplyReaderFeed(reader,(char*)"+OK\r",4); | |
306 | ret = redisReplyReaderGetReply(reader,&reply); | |
307 | assert(ret == REDIS_OK && reply == NULL); | |
308 | redisReplyReaderFeed(reader,(char*)"\n",1); | |
309 | ret = redisReplyReaderGetReply(reader,&reply); | |
310 | test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); | |
311 | redisReplyReaderFree(reader); | |
312 | } | |
313 | ||
314 | static void test_throughput() { | |
315 | int i; | |
316 | long long t1, t2; | |
317 | redisContext *c = blocking_context; | |
318 | redisReply **replies; | |
319 | ||
320 | test("Throughput:\n"); | |
321 | for (i = 0; i < 500; i++) | |
322 | freeReplyObject(redisCommand(c,"LPUSH mylist foo")); | |
323 | ||
324 | replies = malloc(sizeof(redisReply*)*1000); | |
325 | t1 = usec(); | |
326 | for (i = 0; i < 1000; i++) { | |
327 | replies[i] = redisCommand(c,"PING"); | |
328 | assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); | |
329 | } | |
330 | t2 = usec(); | |
331 | for (i = 0; i < 1000; i++) freeReplyObject(replies[i]); | |
332 | free(replies); | |
333 | printf("\t(1000x PING: %.2fs)\n", (t2-t1)/1000000.0); | |
334 | ||
335 | replies = malloc(sizeof(redisReply*)*1000); | |
336 | t1 = usec(); | |
337 | for (i = 0; i < 1000; i++) { | |
338 | replies[i] = redisCommand(c,"LRANGE mylist 0 499"); | |
339 | assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); | |
340 | assert(replies[i] != NULL && replies[i]->elements == 500); | |
341 | } | |
342 | t2 = usec(); | |
343 | for (i = 0; i < 1000; i++) freeReplyObject(replies[i]); | |
344 | free(replies); | |
345 | printf("\t(1000x LRANGE with 500 elements: %.2fs)\n", (t2-t1)/1000000.0); | |
346 | } | |
347 | ||
348 | static void cleanup() { | |
349 | redisContext *c = blocking_context; | |
350 | redisReply *reply; | |
351 | ||
352 | /* Make sure we're on DB 9 */ | |
353 | reply = redisCommand(c,"SELECT 9"); | |
354 | assert(reply != NULL); freeReplyObject(reply); | |
355 | reply = redisCommand(c,"FLUSHDB"); | |
356 | assert(reply != NULL); freeReplyObject(reply); | |
357 | redisFree(c); | |
358 | } | |
359 | ||
360 | // static long __test_callback_flags = 0; | |
361 | // static void __test_callback(redisContext *c, void *privdata) { | |
362 | // ((void)c); | |
363 | // /* Shift to detect execution order */ | |
364 | // __test_callback_flags <<= 8; | |
365 | // __test_callback_flags |= (long)privdata; | |
366 | // } | |
367 | // | |
368 | // static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) { | |
369 | // ((void)c); | |
370 | // /* Shift to detect execution order */ | |
371 | // __test_callback_flags <<= 8; | |
372 | // __test_callback_flags |= (long)privdata; | |
373 | // if (reply) freeReplyObject(reply); | |
374 | // } | |
375 | // | |
376 | // static redisContext *__connect_nonblock() { | |
377 | // /* Reset callback flags */ | |
378 | // __test_callback_flags = 0; | |
379 | // return redisConnectNonBlock("127.0.0.1", 6379, NULL); | |
380 | // } | |
381 | // | |
382 | // static void test_nonblocking_connection() { | |
383 | // redisContext *c; | |
384 | // int wdone = 0; | |
385 | // | |
386 | // test("Calls command callback when command is issued: "); | |
387 | // c = __connect_nonblock(); | |
388 | // redisSetCommandCallback(c,__test_callback,(void*)1); | |
389 | // redisCommand(c,"PING"); | |
390 | // test_cond(__test_callback_flags == 1); | |
391 | // redisFree(c); | |
392 | // | |
393 | // test("Calls disconnect callback on redisDisconnect: "); | |
394 | // c = __connect_nonblock(); | |
395 | // redisSetDisconnectCallback(c,__test_callback,(void*)2); | |
396 | // redisDisconnect(c); | |
397 | // test_cond(__test_callback_flags == 2); | |
398 | // redisFree(c); | |
399 | // | |
400 | // test("Calls disconnect callback and free callback on redisFree: "); | |
401 | // c = __connect_nonblock(); | |
402 | // redisSetDisconnectCallback(c,__test_callback,(void*)2); | |
403 | // redisSetFreeCallback(c,__test_callback,(void*)4); | |
404 | // redisFree(c); | |
405 | // test_cond(__test_callback_flags == ((2 << 8) | 4)); | |
406 | // | |
407 | // test("redisBufferWrite against empty write buffer: "); | |
408 | // c = __connect_nonblock(); | |
409 | // test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1); | |
410 | // redisFree(c); | |
411 | // | |
412 | // test("redisBufferWrite against not yet connected fd: "); | |
413 | // c = __connect_nonblock(); | |
414 | // redisCommand(c,"PING"); | |
415 | // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && | |
416 | // strncmp(c->error,"write:",6) == 0); | |
417 | // redisFree(c); | |
418 | // | |
419 | // test("redisBufferWrite against closed fd: "); | |
420 | // c = __connect_nonblock(); | |
421 | // redisCommand(c,"PING"); | |
422 | // redisDisconnect(c); | |
423 | // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && | |
424 | // strncmp(c->error,"write:",6) == 0); | |
425 | // redisFree(c); | |
426 | // | |
427 | // test("Process callbacks in the right sequence: "); | |
428 | // c = __connect_nonblock(); | |
429 | // redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING"); | |
430 | // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); | |
431 | // redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING"); | |
432 | // | |
433 | // /* Write output buffer */ | |
434 | // wdone = 0; | |
435 | // while(!wdone) { | |
436 | // usleep(500); | |
437 | // redisBufferWrite(c,&wdone); | |
438 | // } | |
439 | // | |
440 | // /* Read until at least one callback is executed (the 3 replies will | |
441 | // * arrive in a single packet, causing all callbacks to be executed in | |
442 | // * a single pass). */ | |
443 | // while(__test_callback_flags == 0) { | |
444 | // assert(redisBufferRead(c) == REDIS_OK); | |
445 | // redisProcessCallbacks(c); | |
446 | // } | |
447 | // test_cond(__test_callback_flags == 0x010203); | |
448 | // redisFree(c); | |
449 | // | |
450 | // test("redisDisconnect executes pending callbacks with NULL reply: "); | |
451 | // c = __connect_nonblock(); | |
452 | // redisSetDisconnectCallback(c,__test_callback,(void*)1); | |
453 | // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); | |
454 | // redisDisconnect(c); | |
455 | // test_cond(__test_callback_flags == 0x0201); | |
456 | // redisFree(c); | |
457 | // } | |
458 | ||
459 | int main(int argc, char **argv) { | |
460 | if (argc > 1) { | |
461 | if (strcmp(argv[1],"-s") == 0) | |
462 | use_unix = 1; | |
463 | } | |
464 | ||
465 | signal(SIGPIPE, SIG_IGN); | |
466 | test_format_commands(); | |
467 | test_blocking_connection(); | |
468 | test_reply_reader(); | |
469 | // test_nonblocking_connection(); | |
470 | test_throughput(); | |
471 | cleanup(); | |
472 | ||
473 | if (fails == 0) { | |
474 | printf("ALL TESTS PASSED\n"); | |
475 | } else { | |
476 | printf("*** %d TESTS FAILED ***\n", fails); | |
477 | } | |
478 | return 0; | |
479 | } |