]> git.proxmox.com Git - mirror_frr.git/blob - tests/lib/cli/test_commands.c
Merge pull request #12837 from donaldsharp/unlikely_routemap
[mirror_frr.git] / tests / lib / cli / test_commands.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Test code for lib/command.c
4 *
5 * Copyright (C) 2013 by Open Source Routing.
6 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
7 *
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.
11 *
12 * The output is currently not validated but only logged. It can
13 * be diffed to find regressions between versions.
14 */
15
16 #define REALLY_NEED_PLAIN_GETOPT 1
17
18 #include <zebra.h>
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23
24 #include "command.h"
25 #include "memory.h"
26 #include "vector.h"
27 #include "prng.h"
28
29 extern vector cmdvec;
30 extern struct cmd_node vty_node;
31 extern void test_init_cmd(void); /* provided in test-commands-defun.c */
32
33 struct event_loop *master; /* dummy for libfrr*/
34
35 static vector test_cmds;
36 static char test_buf[32768];
37
38 static struct cmd_node bgp_node = {
39 .name = "bgp",
40 .node = BGP_NODE,
41 .parent_node = CONFIG_NODE,
42 .prompt = "%s(config-router)# ",
43 };
44
45 static struct cmd_node rip_node = {
46 .name = "rip",
47 .node = RIP_NODE,
48 .parent_node = CONFIG_NODE,
49 .prompt = "%s(config-router)# ",
50 };
51
52 static struct cmd_node isis_node = {
53 .name = "isis",
54 .node = ISIS_NODE,
55 .parent_node = CONFIG_NODE,
56 .prompt = "%s(config-router)# ",
57 };
58
59 static struct cmd_node interface_node = {
60 .name = "interface",
61 .node = INTERFACE_NODE,
62 .parent_node = CONFIG_NODE,
63 .prompt = "%s(config-if)# ",
64 };
65
66 static struct cmd_node rmap_node = {
67 .name = "routemap",
68 .node = RMAP_NODE,
69 .parent_node = CONFIG_NODE,
70 .prompt = "%s(config-route-map)# ",
71 };
72
73 static struct cmd_node zebra_node = {
74 .name = "zebra",
75 .node = ZEBRA_NODE,
76 .parent_node = CONFIG_NODE,
77 .prompt = "%s(config-router)# ",
78 };
79
80 static struct cmd_node bgp_vpnv4_node = {
81 .name = "bgp vpnv4",
82 .node = BGP_VPNV4_NODE,
83 .parent_node = BGP_NODE,
84 .prompt = "%s(config-router-af)# ",
85 };
86
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)# ",
92 };
93
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)# ",
99 };
100
101 static struct cmd_node bgp_ipv6_node = {
102 .name = "bgp ipv6",
103 .node = BGP_IPV6_NODE,
104 .parent_node = BGP_NODE,
105 .prompt = "%s(config-router-af)# ",
106 };
107
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)# ",
113 };
114
115 static struct cmd_node ospf_node = {
116 .name = "ospf",
117 .node = OSPF_NODE,
118 .parent_node = CONFIG_NODE,
119 .prompt = "%s(config-router)# ",
120 };
121
122 static struct cmd_node ripng_node = {
123 .name = "ripng",
124 .node = RIPNG_NODE,
125 .parent_node = CONFIG_NODE,
126 .prompt = "%s(config-router)# ",
127 };
128
129 static struct cmd_node ospf6_node = {
130 .name = "ospf6",
131 .node = OSPF6_NODE,
132 .parent_node = CONFIG_NODE,
133 .prompt = "%s(config-ospf6)# ",
134 };
135
136 static struct cmd_node keychain_node = {
137 .name = "keychain",
138 .node = KEYCHAIN_NODE,
139 .parent_node = CONFIG_NODE,
140 .prompt = "%s(config-keychain)# ",
141 };
142
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)# ",
148 };
149
150 static int test_callback(const struct cmd_element *cmd, struct vty *vty,
151 int argc, struct cmd_token *argv[])
152 {
153 int offset;
154 int rv;
155 int i;
156
157 offset = 0;
158 rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string);
159 if (rv < 0)
160 abort();
161
162 offset += rv;
163
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);
167 if (rv < 0)
168 abort();
169 offset += rv;
170 }
171
172 return CMD_SUCCESS;
173 }
174
175 static void test_load(void)
176 {
177 char line[4096];
178
179 test_cmds = vector_init(VECTOR_MIN_SIZE);
180
181 while (fgets(line, sizeof(line), stdin) != NULL) {
182 if (strlen(line))
183 line[strlen(line) - 1] = '\0';
184 if (line[0] == '#')
185 continue;
186 vector_set(test_cmds, XSTRDUP(MTYPE_TMP, line));
187 }
188 }
189
190 static void test_init(void)
191 {
192 unsigned int node;
193 unsigned int i;
194 struct cmd_node *cnode;
195 struct cmd_element *cmd;
196
197 cmd_init(1);
198 nb_init(master, NULL, 0, false);
199
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);
217
218 test_init_cmd();
219
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))
224 != NULL) {
225 cmd->daemon = 0;
226 cmd->func = test_callback;
227 }
228 test_load();
229 vty_init_vtysh();
230 }
231
232 static void test_terminate(void)
233 {
234 unsigned int i;
235
236 vty_terminate();
237 for (i = 0; i < vector_active(test_cmds); i++)
238 XFREE(MTYPE_TMP, vector_slot(test_cmds, i));
239 vector_free(test_cmds);
240 cmd_terminate();
241 nb_terminate();
242 yang_terminate();
243 }
244
245 static void test_run(struct prng *prng, struct vty *vty, const char *cmd,
246 unsigned int edit_dist, unsigned int node_index,
247 int verbose)
248 {
249 const char *test_str;
250 vector vline;
251 int ret;
252 unsigned int i;
253 char **completions;
254 unsigned int j;
255 struct cmd_node *cnode;
256 vector descriptions;
257 int appended_null;
258 int no_match;
259
260 test_str = prng_fuzz(
261 prng, cmd,
262 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /",
263 edit_dist);
264 vline = cmd_make_strvec(test_str);
265
266 if (vline == NULL)
267 return;
268
269 appended_null = 0;
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)
273 continue;
274
275 if (appended_null) {
276 vector_unset(vline, vector_active(vline) - 1);
277 appended_null = 0;
278 }
279 vty->node = cnode->node;
280 test_buf[0] = '\0';
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') ? ", " : "",
287 test_buf);
288
289 vty->node = cnode->node;
290 test_buf[0] = '\0';
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') ? ", " : "",
296 test_buf);
297
298 if (isspace((unsigned char)test_str[
299 strlen(test_str) - 1])) {
300 vector_set(vline, NULL);
301 appended_null = 1;
302 }
303
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,
308 cnode->node, ret);
309 if (completions != NULL) {
310 for (j = 0; completions[j] != NULL; j++) {
311 printf(" '%s'\n", completions[j]);
312 XFREE(MTYPE_TMP, completions[j]);
313 }
314 XFREE(MTYPE_TMP, completions);
315 }
316
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,
321 cnode->node, ret);
322 if (descriptions != NULL) {
323 for (j = 0; j < vector_active(descriptions);
324 j++) {
325 struct cmd_token *ct =
326 vector_slot(descriptions, j);
327 printf(" '%s' '%s'\n", ct->text,
328 ct->desc);
329 }
330 vector_free(descriptions);
331 }
332 }
333 cmd_free_strvec(vline);
334 }
335
336 int main(int argc, char **argv)
337 {
338 int opt;
339 struct prng *prng;
340 struct vty *vty;
341 unsigned int edit_distance;
342 unsigned int max_edit_distance;
343 unsigned int node_index;
344 int verbose;
345 unsigned int test_cmd;
346 unsigned int iteration;
347 unsigned int num_iterations;
348
349 max_edit_distance = 3;
350 node_index = -1;
351 verbose = 0;
352
353 while ((opt = getopt(argc, argv, "e:n:v")) != -1) {
354 switch (opt) {
355 case 'e':
356 max_edit_distance = atoi(optarg);
357 break;
358 case 'n':
359 node_index = atoi(optarg);
360 break;
361 case 'v':
362 verbose++;
363 break;
364 default:
365 fprintf(stderr,
366 "Usage: %s [-e <edit_dist>] [-n <node_idx>] [-v]\n",
367 argv[0]);
368 exit(1);
369 break;
370 }
371 }
372
373 test_init();
374 prng = prng_new(0);
375
376 vty = vty_new();
377 vty->type = VTY_TERM;
378
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;
382 edit_distance++) {
383 num_iterations = 1 << edit_distance;
384 num_iterations *= num_iterations * num_iterations;
385
386 for (iteration = 0; iteration < num_iterations;
387 iteration++)
388 test_run(prng, vty,
389 vector_slot(test_cmds, test_cmd),
390 edit_distance, node_index, verbose);
391 }
392 fprintf(stderr, "\r%u/%u", test_cmd + 1,
393 vector_active(test_cmds));
394 }
395 fprintf(stderr, "\nDone.\n");
396
397 vty_close(vty);
398 prng_free(prng);
399 test_terminate();
400 return 0;
401 }