LCOV - code coverage report
Current view: top level - spdk/test/unit/lib/jsonrpc/jsonrpc_server.c - jsonrpc_server_ut.c (source / functions) Hit Total Coverage
Test: Combined Lines: 151 151 100.0 %
Date: 2024-07-10 11:15:48 Functions: 8 8 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 59 108 54.6 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2016 Intel Corporation.
       3                 :            :  *   All rights reserved.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include "spdk/stdinc.h"
       7                 :            : 
       8                 :            : #include "spdk_internal/cunit.h"
       9                 :            : 
      10                 :            : #include "jsonrpc/jsonrpc_server.c"
      11                 :            : 
      12                 :            : static struct spdk_jsonrpc_request *g_request;
      13                 :            : static int g_parse_error;
      14                 :            : const struct spdk_json_val *g_method;
      15                 :            : const struct spdk_json_val *g_params;
      16                 :            : 
      17                 :            : const struct spdk_json_val *g_cur_param;
      18                 :            : 
      19                 :            : #define PARSE_PASS(in, trailing) \
      20                 :            :         CU_ASSERT(g_cur_param == NULL); \
      21                 :            :         g_cur_param = NULL; \
      22                 :            :         CU_ASSERT(jsonrpc_parse_request(conn, in, sizeof(in) - 1) == sizeof(in) - sizeof(trailing))
      23                 :            : 
      24                 :            : #define REQ_BEGIN(expected_error) \
      25                 :            :         if (expected_error != 0 ) { \
      26                 :            :                 CU_ASSERT(g_parse_error == expected_error); \
      27                 :            :                 CU_ASSERT(g_params == NULL); \
      28                 :            :         }
      29                 :            : 
      30                 :            : #define PARSE_FAIL(in) \
      31                 :            :         CU_ASSERT(jsonrpc_parse_request(conn, in, sizeof(in) - 1) < 0);
      32                 :            : 
      33                 :            : #define REQ_BEGIN_VALID() \
      34                 :            :                 REQ_BEGIN(0); \
      35                 :            :                 SPDK_CU_ASSERT_FATAL(g_params != NULL);
      36                 :            : 
      37                 :            : #define REQ_BEGIN_INVALID(expected_error) \
      38                 :            :         REQ_BEGIN(expected_error); \
      39                 :            :         REQ_METHOD_MISSING(); \
      40                 :            :         REQ_ID_MISSING(); \
      41                 :            :         REQ_PARAMS_MISSING()
      42                 :            : 
      43                 :            : 
      44                 :            : #define REQ_METHOD(name) \
      45                 :            :         CU_ASSERT(g_method && spdk_json_strequal(g_method, name) == true)
      46                 :            : 
      47                 :            : #define REQ_METHOD_MISSING() \
      48                 :            :         CU_ASSERT(g_method == NULL)
      49                 :            : 
      50                 :            : #define REQ_ID_NUM(num) \
      51                 :            :         CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NUMBER); \
      52                 :            :         CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, sizeof(num) - 1) == 0)
      53                 :            : 
      54                 :            : 
      55                 :            : #define REQ_ID_STRING(str) \
      56                 :            :         CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_STRING); \
      57                 :            :         CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, strlen(num) - 1) == 0))
      58                 :            : 
      59                 :            : #define REQ_ID_NULL() \
      60                 :            :         CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NULL)
      61                 :            : 
      62                 :            : #define REQ_ID_MISSING() \
      63                 :            :         CU_ASSERT(g_request->id == NULL)
      64                 :            : 
      65                 :            : #define REQ_PARAMS_MISSING() \
      66                 :            :         CU_ASSERT(g_params == NULL)
      67                 :            : 
      68                 :            : #define REQ_PARAMS_BEGIN() \
      69                 :            :         SPDK_CU_ASSERT_FATAL(g_params != NULL); \
      70                 :            :         CU_ASSERT(g_cur_param == NULL); \
      71                 :            :         g_cur_param = g_params
      72                 :            : 
      73                 :            : #define PARAM_ARRAY_BEGIN() \
      74                 :            :         CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
      75                 :            :         g_cur_param++
      76                 :            : 
      77                 :            : #define PARAM_ARRAY_END() \
      78                 :            :         CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_END); \
      79                 :            :         g_cur_param++
      80                 :            : 
      81                 :            : #define PARAM_OBJECT_BEGIN() \
      82                 :            :         CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
      83                 :            :         g_cur_param++
      84                 :            : 
      85                 :            : #define PARAM_OBJECT_END() \
      86                 :            :         CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_END); \
      87                 :            :         g_cur_param++
      88                 :            : 
      89                 :            : #define PARAM_NUM(num) \
      90                 :            :         CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NUMBER); \
      91                 :            :         CU_ASSERT(g_cur_param->len == sizeof(num) - 1); \
      92                 :            :         CU_ASSERT(memcmp(g_cur_param->start, num, sizeof(num) - 1) == 0); \
      93                 :            :         g_cur_param++
      94                 :            : 
      95                 :            : #define PARAM_NAME(str) \
      96                 :            :         CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NAME); \
      97                 :            :         CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
      98                 :            :         CU_ASSERT(g_cur_param && memcmp(g_cur_param->start, str, sizeof(str) - 1) == 0); \
      99                 :            :         g_cur_param++
     100                 :            : 
     101                 :            : #define PARAM_STRING(str) \
     102                 :            :         CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_STRING); \
     103                 :            :         CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
     104                 :            :         CU_ASSERT(memcmp(g_cur_param->start, str, g_params->len) == 0); \
     105                 :            :         g_cur_param++
     106                 :            : 
     107                 :            : #define FREE_REQUEST() \
     108                 :            :         ut_jsonrpc_free_request(g_request, g_parse_error); \
     109                 :            :         g_request = NULL; \
     110                 :            :         g_cur_param = NULL; \
     111                 :            :         g_parse_error = 0; \
     112                 :            :         g_method = NULL; \
     113                 :            :         g_cur_param = g_params = NULL
     114                 :            : 
     115                 :            : static void
     116                 :        414 : ut_jsonrpc_free_request(struct spdk_jsonrpc_request *request, int err)
     117                 :            : {
     118                 :            :         struct spdk_json_write_ctx *w;
     119                 :            : 
     120         [ +  + ]:        414 :         if (!request) {
     121                 :        324 :                 return;
     122                 :            :         }
     123                 :            : 
     124                 :            :         /* Need to emulate response to get the response write contex free */
     125         [ +  + ]:         90 :         if (err == 0) {
     126                 :         36 :                 w = spdk_jsonrpc_begin_result(request);
     127                 :         36 :                 spdk_json_write_string(w, "UT PASS response");
     128                 :         36 :                 spdk_jsonrpc_end_result(request, w);
     129                 :            :         } else {
     130                 :         54 :                 spdk_jsonrpc_send_error_response_fmt(request, err, "UT error response");
     131                 :            :         }
     132                 :            : 
     133                 :         90 :         jsonrpc_free_request(request);
     134                 :            : }
     135                 :            : 
     136                 :            : static void
     137                 :         90 : ut_handle(struct spdk_jsonrpc_request *request, int error, const struct spdk_json_val *method,
     138                 :            :           const struct spdk_json_val *params)
     139                 :            : {
     140                 :         90 :         CU_ASSERT(g_request == NULL);
     141                 :         90 :         g_request = request;
     142                 :         90 :         g_parse_error = error;
     143                 :         90 :         g_method = method;
     144                 :         90 :         g_params = params;
     145                 :         90 : }
     146                 :            : 
     147                 :            : void
     148                 :         54 : jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error)
     149                 :            : {
     150                 :         54 :         ut_handle(request, error, NULL, NULL);
     151                 :         54 : }
     152                 :            : 
     153                 :            : void
     154                 :         36 : jsonrpc_server_handle_request(struct spdk_jsonrpc_request *request,
     155                 :            :                               const struct spdk_json_val *method, const struct spdk_json_val *params)
     156                 :            : {
     157                 :         36 :         ut_handle(request, 0, method, params);
     158                 :         36 : }
     159                 :            : 
     160                 :            : void
     161                 :         90 : jsonrpc_server_send_response(struct spdk_jsonrpc_request *request)
     162                 :            : {
     163                 :         90 : }
     164                 :            : 
     165                 :            : static void
     166                 :          6 : test_parse_request(void)
     167                 :            : {
     168                 :            :         struct spdk_jsonrpc_server *server;
     169                 :            :         struct spdk_jsonrpc_server_conn *conn;
     170                 :            : 
     171                 :          6 :         server = calloc(1, sizeof(*server));
     172         [ -  + ]:          6 :         SPDK_CU_ASSERT_FATAL(server != NULL);
     173                 :            : 
     174                 :          6 :         conn = calloc(1, sizeof(*conn));
     175         [ -  + ]:          6 :         SPDK_CU_ASSERT_FATAL(conn != NULL);
     176         [ -  + ]:          6 :         pthread_spin_init(&conn->queue_lock, PTHREAD_PROCESS_PRIVATE);
     177                 :          6 :         STAILQ_INIT(&conn->outstanding_queue);
     178                 :            : 
     179                 :          6 :         conn->server = server;
     180                 :            : 
     181                 :            :         /* rpc call with no parameters. */
     182                 :          6 :         PARSE_PASS("{   }", "");
     183                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
     184                 :          6 :         FREE_REQUEST();
     185                 :            : 
     186                 :            :         /* rpc call with method that is not a string. */
     187                 :          6 :         PARSE_PASS("{\"jsonrpc\":\"2.0\", \"method\": null  }", "");
     188                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
     189                 :          6 :         FREE_REQUEST();
     190                 :            : 
     191                 :            :         /* rpc call with invalid JSON RPC version. */
     192                 :          6 :         PARSE_PASS("{\"jsonrpc\":\"42\", \"method\": \"subtract\"}", "");
     193                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
     194                 :          6 :         FREE_REQUEST();
     195                 :            : 
     196                 :            :         /* rpc call with embedded zeros. */
     197                 :          6 :         PARSE_FAIL("{\"jsonrpc\":\"2.0\",\"method\":\"foo\",\"params\":{\"bar\": \"\0\0baz\"}}");
     198                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
     199                 :          6 :         FREE_REQUEST();
     200                 :            : 
     201                 :            :         /* rpc call with positional parameters */
     202                 :          6 :         PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", "");
     203         [ -  + ]:          6 :         REQ_BEGIN_VALID();
     204   [ +  -  +  - ]:          6 :         REQ_METHOD("subtract");
     205   [ +  -  +  -  :          6 :         REQ_ID_NUM("1");
             +  -  +  - ]
     206         [ -  + ]:          6 :         REQ_PARAMS_BEGIN();
     207                 :          6 :         PARAM_ARRAY_BEGIN();
     208         [ -  + ]:          6 :         PARAM_NUM("42");
     209         [ -  + ]:          6 :         PARAM_NUM("23");
     210                 :          6 :         PARAM_ARRAY_END();
     211                 :          6 :         FREE_REQUEST();
     212                 :            : 
     213                 :            :         /* rpc call with named parameters */
     214                 :          6 :         PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": {\"subtrahend\": 23, \"minuend\": 42}, \"id\": 3}",
     215                 :            :                    "");
     216         [ -  + ]:          6 :         REQ_BEGIN_VALID();
     217   [ +  -  +  - ]:          6 :         REQ_METHOD("subtract");
     218   [ +  -  +  -  :          6 :         REQ_ID_NUM("3");
             +  -  +  - ]
     219         [ -  + ]:          6 :         REQ_PARAMS_BEGIN();
     220                 :          6 :         PARAM_OBJECT_BEGIN();
     221   [ +  -  +  +  :          6 :         PARAM_NAME("subtrahend");
                   +  - ]
     222         [ -  + ]:          6 :         PARAM_NUM("23");
     223   [ +  -  +  +  :          6 :         PARAM_NAME("minuend");
                   +  - ]
     224         [ -  + ]:          6 :         PARAM_NUM("42");
     225                 :          6 :         PARAM_OBJECT_END();
     226                 :          6 :         FREE_REQUEST();
     227                 :            : 
     228                 :            :         /* notification */
     229                 :          6 :         PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5]}", "");
     230         [ -  + ]:          6 :         REQ_BEGIN_VALID();
     231   [ +  -  +  - ]:          6 :         REQ_METHOD("update");
     232                 :          6 :         REQ_ID_MISSING();
     233         [ -  + ]:          6 :         REQ_PARAMS_BEGIN();
     234                 :          6 :         PARAM_ARRAY_BEGIN();
     235                 :          6 :         PARAM_NUM("1");
     236                 :          6 :         PARAM_NUM("2");
     237                 :          6 :         PARAM_NUM("3");
     238                 :          6 :         PARAM_NUM("4");
     239                 :          6 :         PARAM_NUM("5");
     240                 :          6 :         PARAM_ARRAY_END();
     241                 :          6 :         FREE_REQUEST();
     242                 :            : 
     243                 :            :         /* notification with explicit NULL ID. This is discouraged by JSON RPC spec but allowed. */
     244                 :          6 :         PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5], \"id\": null}",
     245                 :            :                    "");
     246         [ -  + ]:          6 :         REQ_BEGIN_VALID();
     247   [ +  -  +  - ]:          6 :         REQ_METHOD("update");
     248   [ +  -  +  - ]:          6 :         REQ_ID_NULL();
     249         [ -  + ]:          6 :         REQ_PARAMS_BEGIN();
     250                 :          6 :         PARAM_ARRAY_BEGIN();
     251                 :          6 :         PARAM_NUM("1");
     252                 :          6 :         PARAM_NUM("2");
     253                 :          6 :         PARAM_NUM("3");
     254                 :          6 :         PARAM_NUM("4");
     255                 :          6 :         PARAM_NUM("5");
     256                 :          6 :         PARAM_ARRAY_END();
     257                 :          6 :         FREE_REQUEST();
     258                 :            : 
     259                 :            :         /* invalid JSON */
     260                 :          6 :         PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]");
     261                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
     262                 :          6 :         FREE_REQUEST();
     263                 :            : 
     264                 :            :         /* invalid request (method must be a string; params must be array or object) */
     265                 :          6 :         PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", "");
     266                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
     267                 :          6 :         FREE_REQUEST();
     268                 :            : 
     269                 :            :         /* batch, invalid JSON */
     270                 :          6 :         PARSE_FAIL(
     271                 :            :                 "["
     272                 :            :                 "{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
     273                 :            :                 "{\"jsonrpc\": \"2.0\", \"method\""
     274                 :            :                 "]");
     275                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
     276                 :          6 :         FREE_REQUEST();
     277                 :            : 
     278                 :            :         /* empty array */
     279                 :          6 :         PARSE_PASS("[]", "");
     280                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
     281                 :          6 :         FREE_REQUEST();
     282                 :            : 
     283                 :            :         /* batch - not supported */
     284                 :          6 :         PARSE_PASS(
     285                 :            :                 "["
     286                 :            :                 "{\"jsonrpc\": \"2.0\", \"method\": \"sum\", \"params\": [1,2,4], \"id\": \"1\"},"
     287                 :            :                 "{\"jsonrpc\": \"2.0\", \"method\": \"notify_hello\", \"params\": [7]},"
     288                 :            :                 "{\"jsonrpc\": \"2.0\", \"method\": \"subtract\", \"params\": [42,23], \"id\": \"2\"},"
     289                 :            :                 "{\"foo\": \"boo\"},"
     290                 :            :                 "{\"jsonrpc\": \"2.0\", \"method\": \"foo.get\", \"params\": {\"name\": \"myself\"}, \"id\": \"5\"},"
     291                 :            :                 "{\"jsonrpc\": \"2.0\", \"method\": \"get_data\", \"id\": \"9\"}"
     292                 :            :                 "]", "");
     293                 :            : 
     294                 :          6 :         REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
     295                 :          6 :         FREE_REQUEST();
     296                 :            : 
     297                 :          6 :         CU_ASSERT(conn->outstanding_requests == 0);
     298                 :          6 :         free(conn);
     299                 :          6 :         free(server);
     300                 :          6 : }
     301                 :            : 
     302                 :            : static void
     303                 :          6 : test_parse_request_streaming(void)
     304                 :            : {
     305                 :            :         struct spdk_jsonrpc_server *server;
     306                 :            :         struct spdk_jsonrpc_server_conn *conn;
     307                 :            :         const char *json_req;
     308                 :            :         size_t len, i;
     309                 :            : 
     310                 :          6 :         server = calloc(1, sizeof(*server));
     311         [ -  + ]:          6 :         SPDK_CU_ASSERT_FATAL(server != NULL);
     312                 :            : 
     313                 :          6 :         conn = calloc(1, sizeof(*conn));
     314         [ -  + ]:          6 :         SPDK_CU_ASSERT_FATAL(conn != NULL);
     315         [ -  + ]:          6 :         pthread_spin_init(&conn->queue_lock, PTHREAD_PROCESS_PRIVATE);
     316                 :          6 :         STAILQ_INIT(&conn->outstanding_queue);
     317                 :            : 
     318                 :          6 :         conn->server = server;
     319                 :            : 
     320                 :            : 
     321                 :            :         /*
     322                 :            :          * Two valid requests end to end in the same buffer.
     323                 :            :          * Parse should return the first one and point to the beginning of the second one.
     324                 :            :          */
     325                 :          6 :         PARSE_PASS(
     326                 :            :                 "{\"jsonrpc\":\"2.0\",\"method\":\"a\",\"params\":[1],\"id\":1}"
     327                 :            :                 "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}",
     328                 :            :                 "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}");
     329                 :            : 
     330         [ -  + ]:          6 :         REQ_BEGIN_VALID();
     331   [ +  -  +  - ]:          6 :         REQ_METHOD("a");
     332   [ +  -  +  -  :          6 :         REQ_ID_NUM("1");
             +  -  +  - ]
     333         [ -  + ]:          6 :         REQ_PARAMS_BEGIN();
     334                 :          6 :         PARAM_ARRAY_BEGIN();
     335                 :          6 :         PARAM_NUM("1");
     336                 :          6 :         PARAM_ARRAY_END();
     337                 :          6 :         FREE_REQUEST();
     338                 :            : 
     339                 :            :         /* Partial (but not invalid) requests - parse should not consume anything. */
     340                 :          6 :         json_req = "    {\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}";
     341         [ -  + ]:          6 :         len = strlen(json_req);
     342                 :            : 
     343                 :            :         /* Try every partial length up to the full request length */
     344         [ +  + ]:        330 :         for (i = 0; i < len; i++) {
     345                 :        324 :                 int rc = jsonrpc_parse_request(conn, json_req, i);
     346                 :            :                 /* Partial request - no data consumed */
     347                 :        324 :                 CU_ASSERT(rc == 0);
     348                 :        324 :                 CU_ASSERT(g_request == NULL);
     349                 :            : 
     350                 :            :                 /* In case of failed, don't fload console with useless CU assert fails. */
     351                 :        324 :                 FREE_REQUEST();
     352                 :            :         }
     353                 :            : 
     354                 :            :         /* Verify that full request can be parsed successfully */
     355                 :          6 :         CU_ASSERT(jsonrpc_parse_request(conn, json_req, len) == (ssize_t)len);
     356                 :          6 :         FREE_REQUEST();
     357                 :            : 
     358                 :          6 :         CU_ASSERT(conn->outstanding_requests == 0);
     359                 :          6 :         free(conn);
     360                 :          6 :         free(server);
     361                 :          6 : }
     362                 :            : 
     363                 :            : int
     364                 :          6 : main(int argc, char **argv)
     365                 :            : {
     366                 :          6 :         CU_pSuite       suite = NULL;
     367                 :            :         unsigned int    num_failures;
     368                 :            : 
     369                 :          6 :         CU_initialize_registry();
     370                 :            : 
     371                 :          6 :         suite = CU_add_suite("jsonrpc", NULL, NULL);
     372                 :            : 
     373                 :          6 :         CU_ADD_TEST(suite, test_parse_request);
     374                 :          6 :         CU_ADD_TEST(suite, test_parse_request_streaming);
     375                 :            : 
     376                 :          6 :         num_failures = spdk_ut_run_tests(argc, argv, NULL);
     377                 :            : 
     378                 :          6 :         CU_cleanup_registry();
     379                 :            : 
     380                 :            :         /* This is for ASAN. Don't know why but if pointer is left in global variable
     381                 :            :          * it won't be detected as leak. */
     382                 :          6 :         g_request = NULL;
     383                 :          6 :         return num_failures;
     384                 :            : }

Generated by: LCOV version 1.14