1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Test code for lib/command.c
5 * Copyright (C) 2013 by Open Source Routing.
6 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
8 * This program reads in a list of commandlines from stdin
9 * and calls all the public functions of lib/command.c for
10 * both the given command lines and fuzzed versions thereof.
12 * The output is currently not validated but only logged. It can
13 * be diffed to find regressions between versions.
16 #define REALLY_NEED_PLAIN_GETOPT 1
30 extern struct cmd_node vty_node
;
31 extern void test_init_cmd(void); /* provided in test-commands-defun.c */
33 struct event_loop
*master
; /* dummy for libfrr*/
35 static vector test_cmds
;
36 static char test_buf
[32768];
38 static struct cmd_node bgp_node
= {
41 .parent_node
= CONFIG_NODE
,
42 .prompt
= "%s(config-router)# ",
45 static struct cmd_node rip_node
= {
48 .parent_node
= CONFIG_NODE
,
49 .prompt
= "%s(config-router)# ",
52 static struct cmd_node isis_node
= {
55 .parent_node
= CONFIG_NODE
,
56 .prompt
= "%s(config-router)# ",
59 static struct cmd_node interface_node
= {
61 .node
= INTERFACE_NODE
,
62 .parent_node
= CONFIG_NODE
,
63 .prompt
= "%s(config-if)# ",
66 static struct cmd_node rmap_node
= {
69 .parent_node
= CONFIG_NODE
,
70 .prompt
= "%s(config-route-map)# ",
73 static struct cmd_node zebra_node
= {
76 .parent_node
= CONFIG_NODE
,
77 .prompt
= "%s(config-router)# ",
80 static struct cmd_node bgp_vpnv4_node
= {
82 .node
= BGP_VPNV4_NODE
,
83 .parent_node
= BGP_NODE
,
84 .prompt
= "%s(config-router-af)# ",
87 static struct cmd_node bgp_ipv4_node
= {
88 .name
= "bgp ipv4 unicast",
89 .node
= BGP_IPV4_NODE
,
90 .parent_node
= BGP_NODE
,
91 .prompt
= "%s(config-router-af)# ",
94 static struct cmd_node bgp_ipv4m_node
= {
95 .name
= "bgp ipv4 multicast",
96 .node
= BGP_IPV4M_NODE
,
97 .parent_node
= BGP_NODE
,
98 .prompt
= "%s(config-router-af)# ",
101 static struct cmd_node bgp_ipv6_node
= {
103 .node
= BGP_IPV6_NODE
,
104 .parent_node
= BGP_NODE
,
105 .prompt
= "%s(config-router-af)# ",
108 static struct cmd_node bgp_ipv6m_node
= {
109 .name
= "bgp ipv6 multicast",
110 .node
= BGP_IPV6M_NODE
,
111 .parent_node
= BGP_NODE
,
112 .prompt
= "%s(config-router-af)# ",
115 static struct cmd_node ospf_node
= {
118 .parent_node
= CONFIG_NODE
,
119 .prompt
= "%s(config-router)# ",
122 static struct cmd_node ripng_node
= {
125 .parent_node
= CONFIG_NODE
,
126 .prompt
= "%s(config-router)# ",
129 static struct cmd_node ospf6_node
= {
132 .parent_node
= CONFIG_NODE
,
133 .prompt
= "%s(config-ospf6)# ",
136 static struct cmd_node keychain_node
= {
138 .node
= KEYCHAIN_NODE
,
139 .parent_node
= CONFIG_NODE
,
140 .prompt
= "%s(config-keychain)# ",
143 static struct cmd_node keychain_key_node
= {
144 .name
= "keychain key",
145 .node
= KEYCHAIN_KEY_NODE
,
146 .parent_node
= KEYCHAIN_NODE
,
147 .prompt
= "%s(config-keychain-key)# ",
150 static int test_callback(const struct cmd_element
*cmd
, struct vty
*vty
,
151 int argc
, struct cmd_token
*argv
[])
158 rv
= snprintf(test_buf
, sizeof(test_buf
), "'%s'", cmd
->string
);
164 for (i
= 0; i
< argc
; i
++) {
165 rv
= snprintf(test_buf
+ offset
, sizeof(test_buf
) - offset
,
166 "%s'%s'", (i
== 0) ? ": " : ", ", argv
[i
]->arg
);
175 static void test_load(void)
179 test_cmds
= vector_init(VECTOR_MIN_SIZE
);
181 while (fgets(line
, sizeof(line
), stdin
) != NULL
) {
183 line
[strlen(line
) - 1] = '\0';
186 vector_set(test_cmds
, XSTRDUP(MTYPE_TMP
, line
));
190 static void test_init(void)
194 struct cmd_node
*cnode
;
195 struct cmd_element
*cmd
;
198 nb_init(master
, NULL
, 0, false);
200 install_node(&bgp_node
);
201 install_node(&rip_node
);
202 install_node(&interface_node
);
203 install_node(&rmap_node
);
204 install_node(&zebra_node
);
205 install_node(&bgp_vpnv4_node
);
206 install_node(&bgp_ipv4_node
);
207 install_node(&bgp_ipv4m_node
);
208 install_node(&bgp_ipv6_node
);
209 install_node(&bgp_ipv6m_node
);
210 install_node(&ospf_node
);
211 install_node(&ripng_node
);
212 install_node(&ospf6_node
);
213 install_node(&keychain_node
);
214 install_node(&keychain_key_node
);
215 install_node(&isis_node
);
216 install_node(&vty_node
);
220 for (node
= 0; node
< vector_active(cmdvec
); node
++)
221 if ((cnode
= vector_slot(cmdvec
, node
)) != NULL
)
222 for (i
= 0; i
< vector_active(cnode
->cmd_vector
); i
++)
223 if ((cmd
= vector_slot(cnode
->cmd_vector
, i
))
226 cmd
->func
= test_callback
;
232 static void test_terminate(void)
237 for (i
= 0; i
< vector_active(test_cmds
); i
++)
238 XFREE(MTYPE_TMP
, vector_slot(test_cmds
, i
));
239 vector_free(test_cmds
);
245 static void test_run(struct prng
*prng
, struct vty
*vty
, const char *cmd
,
246 unsigned int edit_dist
, unsigned int node_index
,
249 const char *test_str
;
255 struct cmd_node
*cnode
;
260 test_str
= prng_fuzz(
262 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /",
264 vline
= cmd_make_strvec(test_str
);
270 for (i
= 0; i
< vector_active(cmdvec
); i
++)
271 if ((cnode
= vector_slot(cmdvec
, i
)) != NULL
) {
272 if (node_index
!= (unsigned int)-1 && i
!= node_index
)
276 vector_unset(vline
, vector_active(vline
) - 1);
279 vty
->node
= cnode
->node
;
281 ret
= cmd_execute_command(vline
, vty
, NULL
, 0);
282 no_match
= (ret
== CMD_ERR_NO_MATCH
);
283 if (verbose
|| !no_match
)
284 printf("execute relaxed '%s'@%d: rv==%d%s%s\n",
285 test_str
, cnode
->node
, ret
,
286 (test_buf
[0] != '\0') ? ", " : "",
289 vty
->node
= cnode
->node
;
291 ret
= cmd_execute_command_strict(vline
, vty
, NULL
);
292 if (verbose
|| !no_match
)
293 printf("execute strict '%s'@%d: rv==%d%s%s\n",
294 test_str
, cnode
->node
, ret
,
295 (test_buf
[0] != '\0') ? ", " : "",
298 if (isspace((unsigned char)test_str
[
299 strlen(test_str
) - 1])) {
300 vector_set(vline
, NULL
);
304 vty
->node
= cnode
->node
;
305 completions
= cmd_complete_command(vline
, vty
, &ret
);
306 if (verbose
|| !no_match
)
307 printf("complete '%s'@%d: rv==%d\n", test_str
,
309 if (completions
!= NULL
) {
310 for (j
= 0; completions
[j
] != NULL
; j
++) {
311 printf(" '%s'\n", completions
[j
]);
312 XFREE(MTYPE_TMP
, completions
[j
]);
314 XFREE(MTYPE_TMP
, completions
);
317 vty
->node
= cnode
->node
;
318 descriptions
= cmd_describe_command(vline
, vty
, &ret
);
319 if (verbose
|| !no_match
)
320 printf("describe '%s'@%d: rv==%d\n", test_str
,
322 if (descriptions
!= NULL
) {
323 for (j
= 0; j
< vector_active(descriptions
);
325 struct cmd_token
*ct
=
326 vector_slot(descriptions
, j
);
327 printf(" '%s' '%s'\n", ct
->text
,
330 vector_free(descriptions
);
333 cmd_free_strvec(vline
);
336 int main(int argc
, char **argv
)
341 unsigned int edit_distance
;
342 unsigned int max_edit_distance
;
343 unsigned int node_index
;
345 unsigned int test_cmd
;
346 unsigned int iteration
;
347 unsigned int num_iterations
;
349 max_edit_distance
= 3;
353 while ((opt
= getopt(argc
, argv
, "e:n:v")) != -1) {
356 max_edit_distance
= atoi(optarg
);
359 node_index
= atoi(optarg
);
366 "Usage: %s [-e <edit_dist>] [-n <node_idx>] [-v]\n",
377 vty
->type
= VTY_TERM
;
379 fprintf(stderr
, "Progress:\n0/%u", vector_active(test_cmds
));
380 for (test_cmd
= 0; test_cmd
< vector_active(test_cmds
); test_cmd
++) {
381 for (edit_distance
= 0; edit_distance
<= max_edit_distance
;
383 num_iterations
= 1 << edit_distance
;
384 num_iterations
*= num_iterations
* num_iterations
;
386 for (iteration
= 0; iteration
< num_iterations
;
389 vector_slot(test_cmds
, test_cmd
),
390 edit_distance
, node_index
, verbose
);
392 fprintf(stderr
, "\r%u/%u", test_cmd
+ 1,
393 vector_active(test_cmds
));
395 fprintf(stderr
, "\nDone.\n");