Beispiel #1
0
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;
}
Beispiel #2
0
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;
}