Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2021 Intel Corporation.
3 : : * All rights reserved.
4 : : * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : : #include "spdk_internal/cunit.h"
9 : : #include "spdk/jsonrpc.h"
10 : : #include "spdk_internal/mock.h"
11 : : #include "common/lib/test_env.c"
12 : : #include "spdk/log.h"
13 : :
14 : : #include "rpc/rpc.c"
15 : :
16 : : #include "unit/lib/json_mock.c"
17 : :
18 : : static int g_rpc_err;
19 : : void fn_rpc_method_handler(struct spdk_jsonrpc_request *request,
20 : : const struct spdk_json_val *params);
21 : :
22 : 10 : DEFINE_STUB_V(spdk_jsonrpc_end_result, (struct spdk_jsonrpc_request *request,
23 : : struct spdk_json_write_ctx *w));
24 [ - + ]: 10 : DEFINE_STUB(spdk_jsonrpc_begin_result, struct spdk_json_write_ctx *,
25 : : (struct spdk_jsonrpc_request *request), (void *)1);
26 [ # # ]: 0 : DEFINE_STUB(spdk_json_decode_bool, int, (const struct spdk_json_val *val, void *out), 0);
27 [ - + ]: 20 : DEFINE_STUB(spdk_jsonrpc_server_listen, struct spdk_jsonrpc_server *, (int domain, int protocol,
28 : : struct sockaddr *listen_addr, socklen_t addrlen, spdk_jsonrpc_handle_request_fn handle_request),
29 : : (struct spdk_jsonrpc_server *)0Xdeaddead);
30 [ # # ]: 0 : DEFINE_STUB(spdk_jsonrpc_server_poll, int, (struct spdk_jsonrpc_server *server), 0);
31 : 20 : DEFINE_STUB_V(spdk_jsonrpc_server_shutdown, (struct spdk_jsonrpc_server *server));
32 : :
33 : : DECLARE_WRAPPER(open, int, (const char *pathname, int flags, mode_t mode));
34 : : DECLARE_WRAPPER(close, int, (int fd));
35 : : DECLARE_WRAPPER(flock, int, (int fd, int operation));
36 [ + + + + ]: 60 : DEFINE_WRAPPER(open, int, (const char *pathname, int flags, mode_t mode), (pathname, flags, mode));
37 [ - + - + ]: 20 : DEFINE_WRAPPER(close, int, (int fd), (fd));
38 [ - + - + ]: 20 : DEFINE_WRAPPER(flock, int, (int fd, int operation), (fd, operation));
39 : :
40 : : int
41 : 10 : spdk_json_decode_object(const struct spdk_json_val *values,
42 : : const struct spdk_json_object_decoder *decoders, size_t num_decoders, void *out)
43 : : {
44 [ + + ]: 10 : if (values ->type == SPDK_JSON_VAL_INVALID) {
45 : 5 : return 1;
46 : : }
47 : 5 : return 0;
48 : : }
49 : :
50 : : bool
51 : 30 : spdk_json_strequal(const struct spdk_json_val *val, const char *str)
52 : : {
53 : : size_t len;
54 : :
55 [ + + + - ]: 30 : if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) {
56 : 10 : return false;
57 : : }
58 : :
59 [ - + ]: 20 : len = strlen(str);
60 [ + + ]: 20 : if (val->len != len) {
61 : 5 : return false;
62 : : }
63 : :
64 [ - + - + ]: 15 : return memcmp(val->start, str, len) == 0;
65 : : }
66 : :
67 : : void
68 : 15 : spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_request *request,
69 : : int error_code, const char *msg)
70 : : {
71 : 15 : g_rpc_err = error_code;
72 : 15 : }
73 : :
74 : : void
75 : 10 : spdk_jsonrpc_send_error_response_fmt(struct spdk_jsonrpc_request *request,
76 : : int error_code, const char *fmt, ...)
77 : : {
78 : 10 : g_rpc_err = error_code;
79 : 10 : }
80 : :
81 : : void
82 : 5 : fn_rpc_method_handler(struct spdk_jsonrpc_request *request,
83 : : const struct spdk_json_val *params)
84 : : {
85 : 5 : g_rpc_err = 0;
86 : 5 : }
87 : :
88 : : static void
89 : 5 : test_jsonrpc_handler(void)
90 : : {
91 : 5 : struct spdk_jsonrpc_request *request = (struct spdk_jsonrpc_request *)0xdeadbeef;
92 : 5 : struct spdk_json_val method = {};
93 : 5 : struct spdk_json_val params = {};
94 : 5 : char *str = "test";
95 : 5 : struct spdk_rpc_method m = {
96 : : .name = "test",
97 : : };
98 : :
99 : 5 : struct spdk_rpc_method is_alias_of = {
100 : : .name = "aliastest",
101 : : .is_deprecated = false,
102 : : .deprecation_warning_printed = false,
103 : : .func = fn_rpc_method_handler,
104 : : .state_mask = SPDK_RPC_STARTUP,
105 : : };
106 : :
107 : : /* Case 1: Method not found */
108 : 5 : method.type = SPDK_JSON_VAL_INVALID;
109 : 5 : jsonrpc_handler(request, &method, ¶ms);
110 : 5 : CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND);
111 : :
112 : : /* Case 2: Method is alias */
113 : 5 : method.type = SPDK_JSON_VAL_STRING;
114 : 5 : method.start = str;
115 : 5 : method.len = 4;
116 : 5 : m.is_alias_of = &is_alias_of;
117 : 5 : m.is_deprecated = true;
118 : 5 : m.deprecation_warning_printed = false;
119 : 5 : m.state_mask = SPDK_RPC_STARTUP;
120 : 5 : SLIST_INSERT_HEAD(&g_rpc_methods, &m, slist);
121 : :
122 : : /* m->state_mask & g_rpc_state == g_rpc_state */
123 : 5 : g_rpc_err = -1;
124 : 5 : g_rpc_state = SPDK_RPC_STARTUP;
125 : 5 : jsonrpc_handler(request, &method, ¶ms);
126 : 5 : CU_ASSERT(g_rpc_err == 0);
127 : :
128 : : /* g_rpc_state == SPDK_RPC_STARTUP */
129 : 5 : is_alias_of.state_mask = SPDK_RPC_RUNTIME;
130 : 5 : g_rpc_err = -1;
131 : 5 : g_rpc_state = SPDK_RPC_STARTUP;
132 : 5 : jsonrpc_handler(request, &method, ¶ms);
133 : 5 : CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_STATE);
134 : :
135 : : /* SPDK_RPC_RUNTIME is invalid for the aliastest RPC */
136 : 5 : is_alias_of.state_mask = SPDK_RPC_STARTUP;
137 : 5 : g_rpc_err = -1;
138 : 5 : g_rpc_state = SPDK_RPC_RUNTIME;
139 : 5 : jsonrpc_handler(request, &method, ¶ms);
140 : 5 : CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_STATE);
141 : :
142 : 5 : SLIST_REMOVE_HEAD(&g_rpc_methods, slist);
143 : 5 : }
144 : :
145 : : static void
146 : 5 : test_spdk_rpc_is_method_allowed(void)
147 : : {
148 : 5 : const char method[] = "test";
149 : 5 : uint32_t state_mask = SPDK_RPC_STARTUP, m_state_mask;
150 : 5 : struct spdk_rpc_method m = {};
151 : 5 : int rc = 0;
152 : :
153 : : /* Case 1: Expect return -EPERM */
154 : 5 : m.name = method;
155 : 5 : m.state_mask = SPDK_RPC_RUNTIME;
156 : 5 : SLIST_INSERT_HEAD(&g_rpc_methods, &m, slist);
157 : 5 : rc = spdk_rpc_is_method_allowed(method, state_mask);
158 : 5 : CU_ASSERT(rc == -EPERM);
159 : 5 : rc = spdk_rpc_get_method_state_mask(method, &m_state_mask);
160 : 5 : CU_ASSERT(rc == 0);
161 : 5 : CU_ASSERT(m_state_mask == m.state_mask);
162 : :
163 : : /* Case 2: Expect return 0 */
164 : 5 : state_mask = SPDK_RPC_RUNTIME;
165 : 5 : rc = spdk_rpc_is_method_allowed(method, state_mask);
166 : 5 : CU_ASSERT(rc == 0);
167 : :
168 : : /* Case 3: Expect return -ENOENT */
169 : 5 : SLIST_REMOVE_HEAD(&g_rpc_methods, slist);
170 : 5 : rc = spdk_rpc_is_method_allowed(method, state_mask);
171 : 5 : CU_ASSERT(rc == -ENOENT);
172 : 5 : rc = spdk_rpc_get_method_state_mask(method, &m_state_mask);
173 : 5 : CU_ASSERT(rc == -ENOENT);
174 : 5 : }
175 : :
176 : : static void
177 : 5 : test_rpc_get_methods(void)
178 : : {
179 : 5 : struct spdk_jsonrpc_request *request = (struct spdk_jsonrpc_request *)0xbeefbeef;
180 : 5 : struct spdk_json_val params = {};
181 : 5 : struct spdk_rpc_method m = {};
182 : :
183 : : /* Case 1: spdk_json_decode_object failed */
184 : 5 : g_rpc_err = -1;
185 : 5 : params.type = SPDK_JSON_VAL_INVALID;
186 : 5 : rpc_get_methods(request, ¶ms);
187 : 5 : CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_PARAMS);
188 : :
189 : : /* Case 2: Expect pass */
190 : 5 : params.type = SPDK_JSON_VAL_TRUE;
191 : 5 : m.state_mask = SPDK_RPC_RUNTIME;
192 : 5 : g_rpc_state = SPDK_RPC_STARTUP;
193 : 5 : SLIST_INSERT_HEAD(&g_rpc_methods, &m, slist);
194 : 5 : rpc_get_methods(request, ¶ms);
195 : 5 : SLIST_REMOVE_HEAD(&g_rpc_methods, slist);
196 : 5 : }
197 : :
198 : : static void
199 : 5 : test_rpc_spdk_get_version(void)
200 : : {
201 : 5 : struct spdk_jsonrpc_request *request = (struct spdk_jsonrpc_request *)0xdeadbeef;
202 : 5 : struct spdk_json_val params = {};
203 : :
204 : : /* Case 1: spdk_get_version method requires no parameters */
205 : 5 : g_rpc_err = -1;
206 : 5 : params.type = SPDK_JSON_VAL_INVALID;
207 : 5 : rpc_spdk_get_version(request, ¶ms);
208 : 5 : CU_ASSERT(g_rpc_err == SPDK_JSONRPC_ERROR_INVALID_PARAMS);
209 : :
210 : : /* Case 2: Expect pass */
211 : 5 : rpc_spdk_get_version(request, NULL);
212 : 5 : }
213 : :
214 : : static void
215 : 5 : test_spdk_rpc_listen_close(void)
216 : : {
217 : 5 : const char listen_addr[128] = "/var/tmp/spdk-rpc-ut.sock";
218 : 5 : char rpc_lock_path[128] = {};
219 : :
220 : 5 : MOCK_SET(open, 1);
221 : 5 : MOCK_SET(close, 0);
222 : 5 : MOCK_SET(flock, 0);
223 : :
224 : 5 : spdk_rpc_listen(listen_addr);
225 : 5 : snprintf(rpc_lock_path, sizeof(g_rpc_server.lock_path), "%s.lock",
226 : : g_rpc_server.listen_addr_unix.sun_path);
227 : :
228 : 5 : CU_ASSERT(g_rpc_server.listen_addr_unix.sun_family == AF_UNIX);
229 : 5 : CU_ASSERT(strcmp(g_rpc_server.listen_addr_unix.sun_path, listen_addr) == 0);
230 : 5 : CU_ASSERT(strcmp(g_rpc_server.lock_path, rpc_lock_path) == 0);
231 : 5 : CU_ASSERT(g_rpc_server.jsonrpc_server == (struct spdk_jsonrpc_server *)0Xdeaddead);
232 : :
233 : 5 : spdk_rpc_close();
234 : :
235 : 5 : CU_ASSERT(g_rpc_server.listen_addr_unix.sun_path[0] == '\0');
236 : 5 : CU_ASSERT(g_rpc_server.jsonrpc_server == NULL);
237 : 5 : CU_ASSERT(g_rpc_server.lock_fd == -1);
238 : 5 : CU_ASSERT(g_rpc_server.lock_path[0] == '\0');
239 : :
240 [ - - - + ]: 5 : MOCK_CLEAR(open);
241 [ - - - + ]: 5 : MOCK_CLEAR(close);
242 [ - - - + ]: 5 : MOCK_CLEAR(flock);
243 : 5 : }
244 : :
245 : : static void
246 : 5 : test_rpc_run_multiple_servers(void)
247 : : {
248 : 5 : const char listen_addr1[128] = "/var/tmp/spdk-rpc-ut.sock1";
249 : 5 : const char listen_addr2[128] = "/var/tmp/spdk-rpc-ut.sock2";
250 : 5 : const char listen_addr3[128] = "/var/tmp/spdk-rpc-ut.sock3";
251 : : struct spdk_rpc_server *rpc_server1, *rpc_server2, *rpc_server3;
252 : :
253 : 5 : MOCK_SET(open, 1);
254 : 5 : MOCK_SET(close, 0);
255 : 5 : MOCK_SET(flock, 0);
256 : :
257 : 5 : rpc_server1 = spdk_rpc_server_listen(listen_addr1);
258 : 5 : CU_ASSERT(rpc_server1 != NULL);
259 : 5 : rpc_server2 = spdk_rpc_server_listen(listen_addr2);
260 : 5 : CU_ASSERT(rpc_server2 != NULL);
261 : 5 : rpc_server3 = spdk_rpc_server_listen(listen_addr3);
262 : 5 : CU_ASSERT(rpc_server3 != NULL);
263 : :
264 : 5 : spdk_rpc_server_close(rpc_server1);
265 : 5 : spdk_rpc_server_close(rpc_server2);
266 : 5 : spdk_rpc_server_close(rpc_server3);
267 : :
268 [ - - - + ]: 5 : MOCK_CLEAR(open);
269 [ - - - + ]: 5 : MOCK_CLEAR(close);
270 [ - - - + ]: 5 : MOCK_CLEAR(flock);
271 : 5 : }
272 : :
273 : : int
274 : 5 : main(int argc, char **argv)
275 : : {
276 : 5 : CU_pSuite suite = NULL;
277 : : unsigned int num_failures;
278 : :
279 : 5 : CU_initialize_registry();
280 : :
281 : 5 : suite = CU_add_suite("rpc", NULL, NULL);
282 : :
283 : 5 : CU_ADD_TEST(suite, test_jsonrpc_handler);
284 : 5 : CU_ADD_TEST(suite, test_spdk_rpc_is_method_allowed);
285 : 5 : CU_ADD_TEST(suite, test_rpc_get_methods);
286 : 5 : CU_ADD_TEST(suite, test_rpc_spdk_get_version);
287 : 5 : CU_ADD_TEST(suite, test_spdk_rpc_listen_close);
288 : 5 : CU_ADD_TEST(suite, test_rpc_run_multiple_servers);
289 : :
290 : 5 : num_failures = spdk_ut_run_tests(argc, argv, NULL);
291 : 5 : CU_cleanup_registry();
292 : 5 : return num_failures;
293 : : }
|