test.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. #include "fmacros.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <strings.h>
  6. #include <sys/time.h>
  7. #include <assert.h>
  8. #include <unistd.h>
  9. #include <signal.h>
  10. #include <errno.h>
  11. #include "hiredis.h"
  12. enum connection_type {
  13. CONN_TCP,
  14. CONN_UNIX
  15. };
  16. struct config {
  17. enum connection_type type;
  18. struct {
  19. const char *host;
  20. int port;
  21. } tcp;
  22. struct {
  23. const char *path;
  24. } unix;
  25. };
  26. /* The following lines make up our testing "framework" :) */
  27. static int tests = 0, fails = 0;
  28. #define test(_s) { printf("#%02d ", ++tests); printf(_s); }
  29. #define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;}
  30. static long long usec(void) {
  31. struct timeval tv;
  32. gettimeofday(&tv,NULL);
  33. return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
  34. }
  35. static redisContext *select_database(redisContext *c) {
  36. redisReply *reply;
  37. /* Switch to DB 9 for testing, now that we know we can chat. */
  38. reply = redisCommand(c,"SELECT 9");
  39. assert(reply != NULL);
  40. freeReplyObject(reply);
  41. /* Make sure the DB is emtpy */
  42. reply = redisCommand(c,"DBSIZE");
  43. assert(reply != NULL);
  44. if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) {
  45. /* Awesome, DB 9 is empty and we can continue. */
  46. freeReplyObject(reply);
  47. } else {
  48. printf("Database #9 is not empty, test can not continue\n");
  49. exit(1);
  50. }
  51. return c;
  52. }
  53. static void disconnect(redisContext *c) {
  54. redisReply *reply;
  55. /* Make sure we're on DB 9. */
  56. reply = redisCommand(c,"SELECT 9");
  57. assert(reply != NULL);
  58. freeReplyObject(reply);
  59. reply = redisCommand(c,"FLUSHDB");
  60. assert(reply != NULL);
  61. freeReplyObject(reply);
  62. /* Free the context as well. */
  63. redisFree(c);
  64. }
  65. static redisContext *connect(struct config config) {
  66. redisContext *c = NULL;
  67. if (config.type == CONN_TCP) {
  68. c = redisConnect(config.tcp.host, config.tcp.port);
  69. } else if (config.type == CONN_UNIX) {
  70. c = redisConnectUnix(config.unix.path);
  71. } else {
  72. assert(NULL);
  73. }
  74. if (c->err) {
  75. printf("Connection error: %s\n", c->errstr);
  76. exit(1);
  77. }
  78. return select_database(c);
  79. }
  80. static void test_format_commands(void) {
  81. char *cmd;
  82. int len;
  83. test("Format command without interpolation: ");
  84. len = redisFormatCommand(&cmd,"SET foo bar");
  85. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
  86. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  87. free(cmd);
  88. test("Format command with %%s string interpolation: ");
  89. len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
  90. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
  91. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  92. free(cmd);
  93. test("Format command with %%s and an empty string: ");
  94. len = redisFormatCommand(&cmd,"SET %s %s","foo","");
  95. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
  96. len == 4+4+(3+2)+4+(3+2)+4+(0+2));
  97. free(cmd);
  98. test("Format command with an empty string in between proper interpolations: ");
  99. len = redisFormatCommand(&cmd,"SET %s %s","","foo");
  100. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
  101. len == 4+4+(3+2)+4+(0+2)+4+(3+2));
  102. free(cmd);
  103. test("Format command with %%b string interpolation: ");
  104. len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
  105. 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 &&
  106. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  107. free(cmd);
  108. test("Format command with %%b and an empty string: ");
  109. len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0);
  110. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
  111. len == 4+4+(3+2)+4+(3+2)+4+(0+2));
  112. free(cmd);
  113. test("Format command with literal %%: ");
  114. len = redisFormatCommand(&cmd,"SET %% %%");
  115. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
  116. len == 4+4+(3+2)+4+(1+2)+4+(1+2));
  117. free(cmd);
  118. /* Vararg width depends on the type. These tests make sure that the
  119. * width is correctly determined using the format and subsequent varargs
  120. * can correctly be interpolated. */
  121. #define INTEGER_WIDTH_TEST(fmt, type) do { \
  122. type value = 123; \
  123. test("Format command with printf-delegation (" #type "): "); \
  124. len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
  125. test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
  126. len == 4+5+(12+2)+4+(9+2)); \
  127. free(cmd); \
  128. } while(0)
  129. #define FLOAT_WIDTH_TEST(type) do { \
  130. type value = 123.0; \
  131. test("Format command with printf-delegation (" #type "): "); \
  132. len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
  133. test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
  134. len == 4+5+(12+2)+4+(9+2)); \
  135. free(cmd); \
  136. } while(0)
  137. INTEGER_WIDTH_TEST("d", int);
  138. INTEGER_WIDTH_TEST("hhd", char);
  139. INTEGER_WIDTH_TEST("hd", short);
  140. INTEGER_WIDTH_TEST("ld", long);
  141. INTEGER_WIDTH_TEST("lld", long long);
  142. INTEGER_WIDTH_TEST("u", unsigned int);
  143. INTEGER_WIDTH_TEST("hhu", unsigned char);
  144. INTEGER_WIDTH_TEST("hu", unsigned short);
  145. INTEGER_WIDTH_TEST("lu", unsigned long);
  146. INTEGER_WIDTH_TEST("llu", unsigned long long);
  147. FLOAT_WIDTH_TEST(float);
  148. FLOAT_WIDTH_TEST(double);
  149. test("Format command with invalid printf format: ");
  150. len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",3);
  151. test_cond(len == -1);
  152. const char *argv[3];
  153. argv[0] = "SET";
  154. argv[1] = "foo\0xxx";
  155. argv[2] = "bar";
  156. size_t lens[3] = { 3, 7, 3 };
  157. int argc = 3;
  158. test("Format command by passing argc/argv without lengths: ");
  159. len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
  160. test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
  161. len == 4+4+(3+2)+4+(3+2)+4+(3+2));
  162. free(cmd);
  163. test("Format command by passing argc/argv with lengths: ");
  164. len = redisFormatCommandArgv(&cmd,argc,argv,lens);
  165. 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 &&
  166. len == 4+4+(3+2)+4+(7+2)+4+(3+2));
  167. free(cmd);
  168. }
  169. static void test_reply_reader(void) {
  170. redisReader *reader;
  171. void *reply;
  172. int ret;
  173. test("Error handling in reply parser: ");
  174. reader = redisReaderCreate();
  175. redisReaderFeed(reader,(char*)"@foo\r\n",6);
  176. ret = redisReaderGetReply(reader,NULL);
  177. test_cond(ret == REDIS_ERR &&
  178. strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
  179. redisReaderFree(reader);
  180. /* when the reply already contains multiple items, they must be free'd
  181. * on an error. valgrind will bark when this doesn't happen. */
  182. test("Memory cleanup in reply parser: ");
  183. reader = redisReaderCreate();
  184. redisReaderFeed(reader,(char*)"*2\r\n",4);
  185. redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
  186. redisReaderFeed(reader,(char*)"@foo\r\n",6);
  187. ret = redisReaderGetReply(reader,NULL);
  188. test_cond(ret == REDIS_ERR &&
  189. strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0);
  190. redisReaderFree(reader);
  191. test("Set error on nested multi bulks with depth > 2: ");
  192. reader = redisReaderCreate();
  193. redisReaderFeed(reader,(char*)"*1\r\n",4);
  194. redisReaderFeed(reader,(char*)"*1\r\n",4);
  195. redisReaderFeed(reader,(char*)"*1\r\n",4);
  196. redisReaderFeed(reader,(char*)"*1\r\n",4);
  197. ret = redisReaderGetReply(reader,NULL);
  198. test_cond(ret == REDIS_ERR &&
  199. strncasecmp(reader->errstr,"No support for",14) == 0);
  200. redisReaderFree(reader);
  201. test("Works with NULL functions for reply: ");
  202. reader = redisReaderCreate();
  203. reader->fn = NULL;
  204. redisReaderFeed(reader,(char*)"+OK\r\n",5);
  205. ret = redisReaderGetReply(reader,&reply);
  206. test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
  207. redisReaderFree(reader);
  208. test("Works when a single newline (\\r\\n) covers two calls to feed: ");
  209. reader = redisReaderCreate();
  210. reader->fn = NULL;
  211. redisReaderFeed(reader,(char*)"+OK\r",4);
  212. ret = redisReaderGetReply(reader,&reply);
  213. assert(ret == REDIS_OK && reply == NULL);
  214. redisReaderFeed(reader,(char*)"\n",1);
  215. ret = redisReaderGetReply(reader,&reply);
  216. test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
  217. redisReaderFree(reader);
  218. test("Don't reset state after protocol error: ");
  219. reader = redisReaderCreate();
  220. reader->fn = NULL;
  221. redisReaderFeed(reader,(char*)"x",1);
  222. ret = redisReaderGetReply(reader,&reply);
  223. assert(ret == REDIS_ERR);
  224. ret = redisReaderGetReply(reader,&reply);
  225. test_cond(ret == REDIS_ERR && reply == NULL);
  226. redisReaderFree(reader);
  227. /* Regression test for issue #45 on GitHub. */
  228. test("Don't do empty allocation for empty multi bulk: ");
  229. reader = redisReaderCreate();
  230. redisReaderFeed(reader,(char*)"*0\r\n",4);
  231. ret = redisReaderGetReply(reader,&reply);
  232. test_cond(ret == REDIS_OK &&
  233. ((redisReply*)reply)->type == REDIS_REPLY_ARRAY &&
  234. ((redisReply*)reply)->elements == 0);
  235. freeReplyObject(reply);
  236. redisReaderFree(reader);
  237. }
  238. static void test_blocking_connection_errors(void) {
  239. redisContext *c;
  240. test("Returns error when host cannot be resolved: ");
  241. c = redisConnect((char*)"idontexist.local", 6379);
  242. test_cond(c->err == REDIS_ERR_OTHER &&
  243. (strcmp(c->errstr,"Name or service not known") == 0 ||
  244. strcmp(c->errstr,"Can't resolve: idontexist.local") == 0));
  245. redisFree(c);
  246. test("Returns error when the port is not open: ");
  247. c = redisConnect((char*)"localhost", 1);
  248. test_cond(c->err == REDIS_ERR_IO &&
  249. strcmp(c->errstr,"Connection refused") == 0);
  250. redisFree(c);
  251. test("Returns error when the unix socket path doesn't accept connections: ");
  252. c = redisConnectUnix((char*)"/tmp/idontexist.sock");
  253. test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */
  254. redisFree(c);
  255. }
  256. static void test_blocking_connection(struct config config) {
  257. redisContext *c;
  258. redisReply *reply;
  259. c = connect(config);
  260. test("Is able to deliver commands: ");
  261. reply = redisCommand(c,"PING");
  262. test_cond(reply->type == REDIS_REPLY_STATUS &&
  263. strcasecmp(reply->str,"pong") == 0)
  264. freeReplyObject(reply);
  265. test("Is a able to send commands verbatim: ");
  266. reply = redisCommand(c,"SET foo bar");
  267. test_cond (reply->type == REDIS_REPLY_STATUS &&
  268. strcasecmp(reply->str,"ok") == 0)
  269. freeReplyObject(reply);
  270. test("%%s String interpolation works: ");
  271. reply = redisCommand(c,"SET %s %s","foo","hello world");
  272. freeReplyObject(reply);
  273. reply = redisCommand(c,"GET foo");
  274. test_cond(reply->type == REDIS_REPLY_STRING &&
  275. strcmp(reply->str,"hello world") == 0);
  276. freeReplyObject(reply);
  277. test("%%b String interpolation works: ");
  278. reply = redisCommand(c,"SET %b %b","foo",3,"hello\x00world",11);
  279. freeReplyObject(reply);
  280. reply = redisCommand(c,"GET foo");
  281. test_cond(reply->type == REDIS_REPLY_STRING &&
  282. memcmp(reply->str,"hello\x00world",11) == 0)
  283. test("Binary reply length is correct: ");
  284. test_cond(reply->len == 11)
  285. freeReplyObject(reply);
  286. test("Can parse nil replies: ");
  287. reply = redisCommand(c,"GET nokey");
  288. test_cond(reply->type == REDIS_REPLY_NIL)
  289. freeReplyObject(reply);
  290. /* test 7 */
  291. test("Can parse integer replies: ");
  292. reply = redisCommand(c,"INCR mycounter");
  293. test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1)
  294. freeReplyObject(reply);
  295. test("Can parse multi bulk replies: ");
  296. freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
  297. freeReplyObject(redisCommand(c,"LPUSH mylist bar"));
  298. reply = redisCommand(c,"LRANGE mylist 0 -1");
  299. test_cond(reply->type == REDIS_REPLY_ARRAY &&
  300. reply->elements == 2 &&
  301. !memcmp(reply->element[0]->str,"bar",3) &&
  302. !memcmp(reply->element[1]->str,"foo",3))
  303. freeReplyObject(reply);
  304. /* m/e with multi bulk reply *before* other reply.
  305. * specifically test ordering of reply items to parse. */
  306. test("Can handle nested multi bulk replies: ");
  307. freeReplyObject(redisCommand(c,"MULTI"));
  308. freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1"));
  309. freeReplyObject(redisCommand(c,"PING"));
  310. reply = (redisCommand(c,"EXEC"));
  311. test_cond(reply->type == REDIS_REPLY_ARRAY &&
  312. reply->elements == 2 &&
  313. reply->element[0]->type == REDIS_REPLY_ARRAY &&
  314. reply->element[0]->elements == 2 &&
  315. !memcmp(reply->element[0]->element[0]->str,"bar",3) &&
  316. !memcmp(reply->element[0]->element[1]->str,"foo",3) &&
  317. reply->element[1]->type == REDIS_REPLY_STATUS &&
  318. strcasecmp(reply->element[1]->str,"pong") == 0);
  319. freeReplyObject(reply);
  320. disconnect(c);
  321. }
  322. static void test_blocking_io_errors(struct config config) {
  323. redisContext *c;
  324. redisReply *reply;
  325. void *_reply;
  326. int major, minor;
  327. /* Connect to target given by config. */
  328. c = connect(config);
  329. {
  330. /* Find out Redis version to determine the path for the next test */
  331. const char *field = "redis_version:";
  332. char *p, *eptr;
  333. reply = redisCommand(c,"INFO");
  334. p = strstr(reply->str,field);
  335. major = strtol(p+strlen(field),&eptr,10);
  336. p = eptr+1; /* char next to the first "." */
  337. minor = strtol(p,&eptr,10);
  338. freeReplyObject(reply);
  339. }
  340. test("Returns I/O error when the connection is lost: ");
  341. reply = redisCommand(c,"QUIT");
  342. if (major >= 2 && minor > 0) {
  343. /* > 2.0 returns OK on QUIT and read() should be issued once more
  344. * to know the descriptor is at EOF. */
  345. test_cond(strcasecmp(reply->str,"OK") == 0 &&
  346. redisGetReply(c,&_reply) == REDIS_ERR);
  347. freeReplyObject(reply);
  348. } else {
  349. test_cond(reply == NULL);
  350. }
  351. /* On 2.0, QUIT will cause the connection to be closed immediately and
  352. * the read(2) for the reply on QUIT will set the error to EOF.
  353. * On >2.0, QUIT will return with OK and another read(2) needed to be
  354. * issued to find out the socket was closed by the server. In both
  355. * conditions, the error will be set to EOF. */
  356. assert(c->err == REDIS_ERR_EOF &&
  357. strcmp(c->errstr,"Server closed the connection") == 0);
  358. redisFree(c);
  359. c = connect(config);
  360. test("Returns I/O error on socket timeout: ");
  361. struct timeval tv = { 0, 1000 };
  362. assert(redisSetTimeout(c,tv) == REDIS_OK);
  363. test_cond(redisGetReply(c,&_reply) == REDIS_ERR &&
  364. c->err == REDIS_ERR_IO && errno == EAGAIN);
  365. redisFree(c);
  366. }
  367. static void test_throughput(struct config config) {
  368. redisContext *c = connect(config);
  369. redisReply **replies;
  370. int i, num;
  371. long long t1, t2;
  372. test("Throughput:\n");
  373. for (i = 0; i < 500; i++)
  374. freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
  375. num = 1000;
  376. replies = malloc(sizeof(redisReply*)*num);
  377. t1 = usec();
  378. for (i = 0; i < num; i++) {
  379. replies[i] = redisCommand(c,"PING");
  380. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
  381. }
  382. t2 = usec();
  383. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  384. free(replies);
  385. printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
  386. replies = malloc(sizeof(redisReply*)*num);
  387. t1 = usec();
  388. for (i = 0; i < num; i++) {
  389. replies[i] = redisCommand(c,"LRANGE mylist 0 499");
  390. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
  391. assert(replies[i] != NULL && replies[i]->elements == 500);
  392. }
  393. t2 = usec();
  394. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  395. free(replies);
  396. printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
  397. num = 10000;
  398. replies = malloc(sizeof(redisReply*)*num);
  399. for (i = 0; i < num; i++)
  400. redisAppendCommand(c,"PING");
  401. t1 = usec();
  402. for (i = 0; i < num; i++) {
  403. assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
  404. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
  405. }
  406. t2 = usec();
  407. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  408. free(replies);
  409. printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
  410. replies = malloc(sizeof(redisReply*)*num);
  411. for (i = 0; i < num; i++)
  412. redisAppendCommand(c,"LRANGE mylist 0 499");
  413. t1 = usec();
  414. for (i = 0; i < num; i++) {
  415. assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
  416. assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
  417. assert(replies[i] != NULL && replies[i]->elements == 500);
  418. }
  419. t2 = usec();
  420. for (i = 0; i < num; i++) freeReplyObject(replies[i]);
  421. free(replies);
  422. printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
  423. disconnect(c);
  424. }
  425. // static long __test_callback_flags = 0;
  426. // static void __test_callback(redisContext *c, void *privdata) {
  427. // ((void)c);
  428. // /* Shift to detect execution order */
  429. // __test_callback_flags <<= 8;
  430. // __test_callback_flags |= (long)privdata;
  431. // }
  432. //
  433. // static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) {
  434. // ((void)c);
  435. // /* Shift to detect execution order */
  436. // __test_callback_flags <<= 8;
  437. // __test_callback_flags |= (long)privdata;
  438. // if (reply) freeReplyObject(reply);
  439. // }
  440. //
  441. // static redisContext *__connect_nonblock() {
  442. // /* Reset callback flags */
  443. // __test_callback_flags = 0;
  444. // return redisConnectNonBlock("127.0.0.1", port, NULL);
  445. // }
  446. //
  447. // static void test_nonblocking_connection() {
  448. // redisContext *c;
  449. // int wdone = 0;
  450. //
  451. // test("Calls command callback when command is issued: ");
  452. // c = __connect_nonblock();
  453. // redisSetCommandCallback(c,__test_callback,(void*)1);
  454. // redisCommand(c,"PING");
  455. // test_cond(__test_callback_flags == 1);
  456. // redisFree(c);
  457. //
  458. // test("Calls disconnect callback on redisDisconnect: ");
  459. // c = __connect_nonblock();
  460. // redisSetDisconnectCallback(c,__test_callback,(void*)2);
  461. // redisDisconnect(c);
  462. // test_cond(__test_callback_flags == 2);
  463. // redisFree(c);
  464. //
  465. // test("Calls disconnect callback and free callback on redisFree: ");
  466. // c = __connect_nonblock();
  467. // redisSetDisconnectCallback(c,__test_callback,(void*)2);
  468. // redisSetFreeCallback(c,__test_callback,(void*)4);
  469. // redisFree(c);
  470. // test_cond(__test_callback_flags == ((2 << 8) | 4));
  471. //
  472. // test("redisBufferWrite against empty write buffer: ");
  473. // c = __connect_nonblock();
  474. // test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);
  475. // redisFree(c);
  476. //
  477. // test("redisBufferWrite against not yet connected fd: ");
  478. // c = __connect_nonblock();
  479. // redisCommand(c,"PING");
  480. // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
  481. // strncmp(c->error,"write:",6) == 0);
  482. // redisFree(c);
  483. //
  484. // test("redisBufferWrite against closed fd: ");
  485. // c = __connect_nonblock();
  486. // redisCommand(c,"PING");
  487. // redisDisconnect(c);
  488. // test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
  489. // strncmp(c->error,"write:",6) == 0);
  490. // redisFree(c);
  491. //
  492. // test("Process callbacks in the right sequence: ");
  493. // c = __connect_nonblock();
  494. // redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING");
  495. // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
  496. // redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING");
  497. //
  498. // /* Write output buffer */
  499. // wdone = 0;
  500. // while(!wdone) {
  501. // usleep(500);
  502. // redisBufferWrite(c,&wdone);
  503. // }
  504. //
  505. // /* Read until at least one callback is executed (the 3 replies will
  506. // * arrive in a single packet, causing all callbacks to be executed in
  507. // * a single pass). */
  508. // while(__test_callback_flags == 0) {
  509. // assert(redisBufferRead(c) == REDIS_OK);
  510. // redisProcessCallbacks(c);
  511. // }
  512. // test_cond(__test_callback_flags == 0x010203);
  513. // redisFree(c);
  514. //
  515. // test("redisDisconnect executes pending callbacks with NULL reply: ");
  516. // c = __connect_nonblock();
  517. // redisSetDisconnectCallback(c,__test_callback,(void*)1);
  518. // redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
  519. // redisDisconnect(c);
  520. // test_cond(__test_callback_flags == 0x0201);
  521. // redisFree(c);
  522. // }
  523. int main(int argc, char **argv) {
  524. struct config cfg = {
  525. .tcp = {
  526. .host = "127.0.0.1",
  527. .port = 6379
  528. },
  529. .unix = {
  530. .path = "/tmp/redis.sock"
  531. }
  532. };
  533. int throughput = 1;
  534. /* Ignore broken pipe signal (for I/O error tests). */
  535. signal(SIGPIPE, SIG_IGN);
  536. /* Parse command line options. */
  537. argv++; argc--;
  538. while (argc) {
  539. if (argc >= 2 && !strcmp(argv[0],"-h")) {
  540. argv++; argc--;
  541. cfg.tcp.host = argv[0];
  542. } else if (argc >= 2 && !strcmp(argv[0],"-p")) {
  543. argv++; argc--;
  544. cfg.tcp.port = atoi(argv[0]);
  545. } else if (argc >= 2 && !strcmp(argv[0],"-s")) {
  546. argv++; argc--;
  547. cfg.unix.path = argv[0];
  548. } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) {
  549. throughput = 0;
  550. } else {
  551. fprintf(stderr, "Invalid argument: %s\n", argv[0]);
  552. exit(1);
  553. }
  554. argv++; argc--;
  555. }
  556. test_format_commands();
  557. test_reply_reader();
  558. test_blocking_connection_errors();
  559. printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port);
  560. cfg.type = CONN_TCP;
  561. test_blocking_connection(cfg);
  562. test_blocking_io_errors(cfg);
  563. if (throughput) test_throughput(cfg);
  564. printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path);
  565. cfg.type = CONN_UNIX;
  566. test_blocking_connection(cfg);
  567. test_blocking_io_errors(cfg);
  568. if (throughput) test_throughput(cfg);
  569. if (fails) {
  570. printf("*** %d TESTS FAILED ***\n", fails);
  571. return 1;
  572. }
  573. printf("ALL TESTS PASSED\n");
  574. return 0;
  575. }