END_TEST /* * request */ START_TEST(test_quit) { #define QUIT "quit" #define SERIALIZED "*1\r\n$4\r\n" QUIT "\r\n" #define INVALID "*2\r\n$4\r\n" QUIT "\r\n$3\r\nnow\r\n" int ret; struct element *el; test_reset(); req->type = REQ_QUIT; el = array_push(req->token); el->type = ELEM_BULK; el->bstr = (struct bstring){sizeof(QUIT) - 1, QUIT}; ret = compose_req(&buf, req); ck_assert_int_eq(ret, sizeof(SERIALIZED) - 1); ck_assert_int_eq(cc_bcmp(buf->rpos, SERIALIZED, ret), 0); el->type = ELEM_UNKNOWN; /* this effectively resets *el */ request_reset(req); ck_assert_int_eq(parse_req(req, buf), PARSE_OK); ck_assert_int_eq(req->type, REQ_QUIT); ck_assert_int_eq(req->token->nelem, 1); el = array_first(req->token); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(cc_bcmp(el->bstr.data, QUIT, sizeof(QUIT) - 1), 0); /* invalid number of arguments */ test_reset(); buf_write(buf, INVALID, sizeof(INVALID) - 1); ck_assert_int_eq(parse_req(req, buf), PARSE_EINVALID); #undef INVALID #undef SERIALIZED #undef QUIT } END_TEST START_TEST(test_ping) { #define PING "ping" #define VAL "hello" #define S_PING "*1\r\n$4\r\n" PING "\r\n" #define S_ECHO "*2\r\n$4\r\n" PING "\r\n$5\r\nhello\r\n" int ret; struct element *el; test_reset(); /* simple ping */ buf_write(buf, S_PING, sizeof(S_PING) - 1); ck_assert_int_eq(parse_req(req, buf), PARSE_OK); ck_assert_int_eq(req->type, REQ_PING); /* ping as echo */ test_reset(); req->type = REQ_PING; el = array_push(req->token); el->type = ELEM_BULK; el->bstr = (struct bstring){sizeof(PING) - 1, PING}; el = array_push(req->token); el->type = ELEM_BULK; el->bstr = (struct bstring){sizeof(VAL) - 1, VAL}; ret = compose_req(&buf, req); ck_assert_int_eq(ret, sizeof(S_ECHO) - 1); ck_assert_int_eq(cc_bcmp(buf->rpos, S_ECHO, ret), 0); el->type = ELEM_UNKNOWN; /* resets *el */ request_reset(req); ck_assert_int_eq(parse_req(req, buf), PARSE_OK); ck_assert_int_eq(req->type, REQ_PING); ck_assert_int_eq(req->token->nelem, 2); el = array_first(req->token); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(cc_bcmp(el->bstr.data, PING, sizeof(PING) - 1), 0); el = array_get(req->token, 1); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(cc_bcmp(el->bstr.data, VAL, sizeof(VAL) - 1), 0); #undef S_ECHO #undef ECHO #undef S_PING #undef QUIT } END_TEST START_TEST(test_unfin_req) { char *token[4] = { "*2\r\n", "*2\r\n$3\r\n", "*2\r\n$3\r\nfoo\r\n", "*2\r\n$3\r\nfoo\r\n$3\r\n", }; for (int i = 0; i < 4; i++) { char *pos; size_t len; len = strlen(token[i]); buf_reset(buf); buf_write(buf, token[i], len); pos = buf->rpos; ck_assert_int_eq(parse_req(req, buf), PARSE_EUNFIN); ck_assert(buf->rpos == pos); } } END_TEST /* * response */ START_TEST(test_ok) { #define OK "OK" #define SERIALIZED "+" OK "\r\n" int ret; struct element *el; test_reset(); rsp->type = ELEM_STR; el = array_push(rsp->token); el->type = ELEM_STR; el->bstr = (struct bstring){sizeof(OK) - 1, OK}; ret = compose_rsp(&buf, rsp); ck_assert_int_eq(ret, sizeof(SERIALIZED) - 1); ck_assert_int_eq(cc_bcmp(buf->rpos, SERIALIZED, ret), 0); el->type = ELEM_UNKNOWN; /* resets *el */ response_reset(rsp); ck_assert_int_eq(parse_rsp(rsp, buf), PARSE_OK); ck_assert_int_eq(rsp->type, ELEM_STR); ck_assert_int_eq(rsp->token->nelem, 1); el = array_first(rsp->token); ck_assert_int_eq(el->type, ELEM_STR); ck_assert_int_eq(cc_bcmp(el->bstr.data, OK, sizeof(OK) - 1), 0); #undef SERIALIZED #undef OK } END_TEST START_TEST(test_array_reply) { #define SERIALIZED "*5\r\n:-10\r\n$-1\r\n-ERR invalid arg\r\n+foo\r\n$5\r\nHELLO\r\n" size_t len = sizeof(SERIALIZED) - 1; struct element *el; test_reset(); buf_write(buf, SERIALIZED, len); ck_assert_int_eq(parse_rsp(rsp, buf), PARSE_OK); ck_assert_int_eq(rsp->type, ELEM_ARRAY); ck_assert_int_eq(rsp->token->nelem, 5); el = array_first(rsp->token); ck_assert_int_eq(el->type, ELEM_INT); el = array_get(rsp->token, 1); ck_assert_int_eq(el->type, ELEM_NIL); el = array_get(rsp->token, 2); ck_assert_int_eq(el->type, ELEM_ERR); el = array_get(rsp->token, 3); ck_assert_int_eq(el->type, ELEM_STR); el = array_get(rsp->token, 4); ck_assert_int_eq(el->type, ELEM_BULK); ck_assert_int_eq(el->bstr.len, 5); ck_assert_int_eq(cc_bcmp(el->bstr.data, "HELLO", 5), 0); ck_assert_int_eq(buf_rsize(buf), 0); ck_assert_int_eq(compose_rsp(&buf, rsp), len); ck_assert_int_eq(buf_rsize(buf), len); ck_assert_int_eq(cc_bcmp(buf->rpos, SERIALIZED, len), 0); #undef SERIALIZED } END_TEST /* * edge cases */ START_TEST(test_empty_buf) { struct element el; test_reset(); ck_assert(!token_is_array(buf)); ck_assert_int_eq(parse_element(&el, buf), PARSE_EUNFIN); ck_assert_int_eq(parse_rsp(rsp, buf), PARSE_EUNFIN); ck_assert_int_eq(parse_req(req, buf), PARSE_EUNFIN); } END_TEST /* * request/response pool */ START_TEST(test_req_pool_basic) { #define POOL_SIZE 10 int i; struct request *reqs[POOL_SIZE]; request_options_st options = { .request_ntoken = {.type = OPTION_TYPE_UINT, .val.vuint = REQ_NTOKEN}, .request_poolsize = {.type = OPTION_TYPE_UINT, .val.vuint = POOL_SIZE}}; request_setup(&options, NULL); for (i = 0; i < POOL_SIZE; i++) { reqs[i] = request_borrow(); ck_assert_msg(reqs[i] != NULL, "expected to borrow a request"); } ck_assert_msg(request_borrow() == NULL, "expected request pool to be depleted"); for (i = 0; i < POOL_SIZE; i++) { request_return(&reqs[i]); ck_assert_msg(reqs[i] == NULL, "expected request to be nulled after return"); } request_teardown(); #undef POOL_SIZE } END_TEST START_TEST(test_rsp_pool_basic) { #define POOL_SIZE 10 int i; struct response *rsps[POOL_SIZE]; response_options_st options = { .response_ntoken = {.type = OPTION_TYPE_UINT, .val.vuint = RSP_NTOKEN}, .response_poolsize = {.type = OPTION_TYPE_UINT, .val.vuint = POOL_SIZE}}; response_setup(&options, NULL); for (i = 0; i < POOL_SIZE; i++) { rsps[i] = response_borrow(); ck_assert_msg(rsps[i] != NULL, "expected to borrow a response"); } ck_assert_msg(response_borrow() == NULL, "expected response pool to be depleted"); for (i = 0; i < POOL_SIZE; i++) { response_return(&rsps[i]); ck_assert_msg(rsps[i] == NULL, "expected response to be nulled after return"); } response_teardown(); #undef POOL_SIZE } END_TEST /* * test suite */ static Suite * redis_suite(void) { Suite *s = suite_create(SUITE_NAME); /* token */ TCase *tc_token = tcase_create("token"); suite_add_tcase(s, tc_token); tcase_add_test(tc_token, test_simple_string); tcase_add_test(tc_token, test_error); tcase_add_test(tc_token, test_integer); tcase_add_test(tc_token, test_bulk_string); tcase_add_test(tc_token, test_array); tcase_add_test(tc_token, test_nil_bulk); tcase_add_test(tc_token, test_unfin_token); /* basic requests */ TCase *tc_request = tcase_create("request"); suite_add_tcase(s, tc_request); tcase_add_test(tc_request, test_quit); tcase_add_test(tc_request, test_ping); tcase_add_test(tc_request, test_unfin_req); /* basic responses */ TCase *tc_response = tcase_create("response"); suite_add_tcase(s, tc_response); tcase_add_test(tc_response, test_ok); tcase_add_test(tc_response, test_array_reply); /* edge cases */ TCase *tc_edge = tcase_create("edge cases"); suite_add_tcase(s, tc_edge); tcase_add_test(tc_edge, test_empty_buf); /* req/rsp objects, pooling */ TCase *tc_pool = tcase_create("request/response pool"); suite_add_tcase(s, tc_pool); tcase_add_test(tc_pool, test_req_pool_basic); tcase_add_test(tc_pool, test_rsp_pool_basic); return s; } /* TODO(yao): move main to a different file, keep most test files main-less */ int main(void) { int nfail; /* setup */ test_setup(); Suite *suite = redis_suite(); SRunner *srunner = srunner_create(suite); srunner_set_log(srunner, DEBUG_LOG); srunner_run_all(srunner, CK_ENV); /* set CK_VEBOSITY in ENV to customize */ nfail = srunner_ntests_failed(srunner); srunner_free(srunner); /* teardown */ test_teardown(); return (nfail == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }
static int send_msg(jrpc_req_pkg_t *req, int evt_id) { #define MAX_RETRIES 2 #define RECV_TIMEO 5000000 /* in useconds */ jrpc_rsp_pkg_t resp; int fd = cmfd; int rc, retries = MAX_RETRIES; fd_set fds; struct timeval timeo; static unsigned int seqnum; char *req_str = NULL; static pthread_mutex_t socket_mutex = PTHREAD_MUTEX_INITIALIZER; char rsp_string[JSONRPC_MAX_STRING_LEN] = {0}; int64 error_code = 0; pthread_mutex_lock(&socket_mutex); if (fd < 0) { exit(-1); } req->id = ++seqnum; if (NULL == (req_str = jrpc_create_req_string(req->id, am_cmd_map[evt_id].cmd_name, req->num_of_params, req->params))) { error_code = -1; goto failed; } printf("------------>evt_id: %d ,", evt_id); printf("%s\n", req_str); if ((rc = socket_send(fd, req_str, strnlen_s(req_str, RSIZE_MAX_STR) + 1)) < 0) { error_code = -1; goto failed; } req->json = NULL; for (;;) { if (--retries < 0) break; FD_ZERO(&fds); FD_SET(fd, &fds); timeo.tv_sec = 0;/*RECV_TIMEO;*/ timeo.tv_usec = RECV_TIMEO; rc = select(fd + 1, &fds, NULL, NULL, &timeo); if (rc <= 0) continue; rc = socket_recv(fd, rsp_string, sizeof(rsp_string)); printf("rsp string:%s.\n", rsp_string); if (-1 == rc || 0 != parse_rsp(rsp_string, &resp)) { error_code = -1; goto failed; } get_node_id_from_rsp(resp.data.result.value_obj, &req->node_id); if (resp.id_type == JSONRPC_ID_TYPE_NORMAL && resp.id == req->id) { if (resp.rsp_type == JSONRPC_RSP_ERROR) { error_code = resp.data.error.code; } else if (resp.rsp_type == JSONRPC_RSP_INVALID) { error_code = -1; } else { if (req_str != NULL) { jrpc_free_string(req_str); req_str = NULL; } pthread_mutex_unlock(&socket_mutex); return 0; } } else if (resp.id < req->id) { /*jrpc_rsp_free(&resp);*/ continue; } else { goto failed; } } failed: if (req_str != NULL) { jrpc_free_string(req_str); req_str = NULL; } jrpc_rsp_pkg_free(&resp); pthread_mutex_unlock(&socket_mutex); return (int)error_code; }