summaryrefslogtreecommitdiffstats
path: root/tests/lib/cli/test_commands.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lib/cli/test_commands.c')
-rw-r--r--tests/lib/cli/test_commands.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/tests/lib/cli/test_commands.c b/tests/lib/cli/test_commands.c
new file mode 100644
index 0000000..ea84120
--- /dev/null
+++ b/tests/lib/cli/test_commands.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test code for lib/command.c
+ *
+ * Copyright (C) 2013 by Open Source Routing.
+ * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This program reads in a list of commandlines from stdin
+ * and calls all the public functions of lib/command.c for
+ * both the given command lines and fuzzed versions thereof.
+ *
+ * The output is currently not validated but only logged. It can
+ * be diffed to find regressions between versions.
+ */
+
+#define REALLY_NEED_PLAIN_GETOPT 1
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "command.h"
+#include "memory.h"
+#include "vector.h"
+#include "prng.h"
+
+extern vector cmdvec;
+extern struct cmd_node vty_node;
+extern void test_init_cmd(void); /* provided in test-commands-defun.c */
+
+struct event_loop *master; /* dummy for libfrr*/
+
+static vector test_cmds;
+static char test_buf[32768];
+
+static struct cmd_node bgp_node = {
+ .name = "bgp",
+ .node = BGP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node rip_node = {
+ .name = "rip",
+ .node = RIP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node isis_node = {
+ .name = "isis",
+ .node = ISIS_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node interface_node = {
+ .name = "interface",
+ .node = INTERFACE_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-if)# ",
+};
+
+static struct cmd_node rmap_node = {
+ .name = "routemap",
+ .node = RMAP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-route-map)# ",
+};
+
+static struct cmd_node zebra_node = {
+ .name = "zebra",
+ .node = ZEBRA_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node bgp_vpnv4_node = {
+ .name = "bgp vpnv4",
+ .node = BGP_VPNV4_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv4_node = {
+ .name = "bgp ipv4 unicast",
+ .node = BGP_IPV4_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv4m_node = {
+ .name = "bgp ipv4 multicast",
+ .node = BGP_IPV4M_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv6_node = {
+ .name = "bgp ipv6",
+ .node = BGP_IPV6_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node bgp_ipv6m_node = {
+ .name = "bgp ipv6 multicast",
+ .node = BGP_IPV6M_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+};
+
+static struct cmd_node ospf_node = {
+ .name = "ospf",
+ .node = OSPF_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node ripng_node = {
+ .name = "ripng",
+ .node = RIPNG_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+};
+
+static struct cmd_node ospf6_node = {
+ .name = "ospf6",
+ .node = OSPF6_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-ospf6)# ",
+};
+
+static struct cmd_node keychain_node = {
+ .name = "keychain",
+ .node = KEYCHAIN_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-keychain)# ",
+};
+
+static struct cmd_node keychain_key_node = {
+ .name = "keychain key",
+ .node = KEYCHAIN_KEY_NODE,
+ .parent_node = KEYCHAIN_NODE,
+ .prompt = "%s(config-keychain-key)# ",
+};
+
+static int test_callback(const struct cmd_element *cmd, struct vty *vty,
+ int argc, struct cmd_token *argv[])
+{
+ int offset;
+ int rv;
+ int i;
+
+ offset = 0;
+ rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string);
+ if (rv < 0)
+ abort();
+
+ offset += rv;
+
+ for (i = 0; i < argc; i++) {
+ rv = snprintf(test_buf + offset, sizeof(test_buf) - offset,
+ "%s'%s'", (i == 0) ? ": " : ", ", argv[i]->arg);
+ if (rv < 0)
+ abort();
+ offset += rv;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void test_load(void)
+{
+ char line[4096];
+
+ test_cmds = vector_init(VECTOR_MIN_SIZE);
+
+ while (fgets(line, sizeof(line), stdin) != NULL) {
+ if (strlen(line))
+ line[strlen(line) - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+ vector_set(test_cmds, XSTRDUP(MTYPE_TMP, line));
+ }
+}
+
+static void test_init(void)
+{
+ unsigned int node;
+ unsigned int i;
+ struct cmd_node *cnode;
+ struct cmd_element *cmd;
+
+ cmd_init(1);
+ nb_init(master, NULL, 0, false);
+
+ install_node(&bgp_node);
+ install_node(&rip_node);
+ install_node(&interface_node);
+ install_node(&rmap_node);
+ install_node(&zebra_node);
+ install_node(&bgp_vpnv4_node);
+ install_node(&bgp_ipv4_node);
+ install_node(&bgp_ipv4m_node);
+ install_node(&bgp_ipv6_node);
+ install_node(&bgp_ipv6m_node);
+ install_node(&ospf_node);
+ install_node(&ripng_node);
+ install_node(&ospf6_node);
+ install_node(&keychain_node);
+ install_node(&keychain_key_node);
+ install_node(&isis_node);
+ install_node(&vty_node);
+
+ test_init_cmd();
+
+ for (node = 0; node < vector_active(cmdvec); node++)
+ if ((cnode = vector_slot(cmdvec, node)) != NULL)
+ for (i = 0; i < vector_active(cnode->cmd_vector); i++)
+ if ((cmd = vector_slot(cnode->cmd_vector, i))
+ != NULL) {
+ cmd->daemon = 0;
+ cmd->func = test_callback;
+ }
+ test_load();
+ vty_init_vtysh();
+}
+
+static void test_terminate(void)
+{
+ unsigned int i;
+
+ vty_terminate();
+ for (i = 0; i < vector_active(test_cmds); i++)
+ XFREE(MTYPE_TMP, vector_slot(test_cmds, i));
+ vector_free(test_cmds);
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+}
+
+static void test_run(struct prng *prng, struct vty *vty, const char *cmd,
+ unsigned int edit_dist, unsigned int node_index,
+ int verbose)
+{
+ const char *test_str;
+ vector vline;
+ int ret;
+ unsigned int i;
+ char **completions;
+ unsigned int j;
+ struct cmd_node *cnode;
+ vector descriptions;
+ int appended_null;
+ int no_match;
+
+ test_str = prng_fuzz(
+ prng, cmd,
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /",
+ edit_dist);
+ vline = cmd_make_strvec(test_str);
+
+ if (vline == NULL)
+ return;
+
+ appended_null = 0;
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((cnode = vector_slot(cmdvec, i)) != NULL) {
+ if (node_index != (unsigned int)-1 && i != node_index)
+ continue;
+
+ if (appended_null) {
+ vector_unset(vline, vector_active(vline) - 1);
+ appended_null = 0;
+ }
+ vty->node = cnode->node;
+ test_buf[0] = '\0';
+ ret = cmd_execute_command(vline, vty, NULL, 0);
+ no_match = (ret == CMD_ERR_NO_MATCH);
+ if (verbose || !no_match)
+ printf("execute relaxed '%s'@%d: rv==%d%s%s\n",
+ test_str, cnode->node, ret,
+ (test_buf[0] != '\0') ? ", " : "",
+ test_buf);
+
+ vty->node = cnode->node;
+ test_buf[0] = '\0';
+ ret = cmd_execute_command_strict(vline, vty, NULL);
+ if (verbose || !no_match)
+ printf("execute strict '%s'@%d: rv==%d%s%s\n",
+ test_str, cnode->node, ret,
+ (test_buf[0] != '\0') ? ", " : "",
+ test_buf);
+
+ if (isspace((unsigned char)test_str[
+ strlen(test_str) - 1])) {
+ vector_set(vline, NULL);
+ appended_null = 1;
+ }
+
+ vty->node = cnode->node;
+ completions = cmd_complete_command(vline, vty, &ret);
+ if (verbose || !no_match)
+ printf("complete '%s'@%d: rv==%d\n", test_str,
+ cnode->node, ret);
+ if (completions != NULL) {
+ for (j = 0; completions[j] != NULL; j++) {
+ printf(" '%s'\n", completions[j]);
+ XFREE(MTYPE_TMP, completions[j]);
+ }
+ XFREE(MTYPE_TMP, completions);
+ }
+
+ vty->node = cnode->node;
+ descriptions = cmd_describe_command(vline, vty, &ret);
+ if (verbose || !no_match)
+ printf("describe '%s'@%d: rv==%d\n", test_str,
+ cnode->node, ret);
+ if (descriptions != NULL) {
+ for (j = 0; j < vector_active(descriptions);
+ j++) {
+ struct cmd_token *ct =
+ vector_slot(descriptions, j);
+ printf(" '%s' '%s'\n", ct->text,
+ ct->desc);
+ }
+ vector_free(descriptions);
+ }
+ }
+ cmd_free_strvec(vline);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ struct prng *prng;
+ struct vty *vty;
+ unsigned int edit_distance;
+ unsigned int max_edit_distance;
+ unsigned int node_index;
+ int verbose;
+ unsigned int test_cmd;
+ unsigned int iteration;
+ unsigned int num_iterations;
+
+ max_edit_distance = 3;
+ node_index = -1;
+ verbose = 0;
+
+ while ((opt = getopt(argc, argv, "e:n:v")) != -1) {
+ switch (opt) {
+ case 'e':
+ max_edit_distance = atoi(optarg);
+ break;
+ case 'n':
+ node_index = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ fprintf(stderr,
+ "Usage: %s [-e <edit_dist>] [-n <node_idx>] [-v]\n",
+ argv[0]);
+ exit(1);
+ break;
+ }
+ }
+
+ test_init();
+ prng = prng_new(0);
+
+ vty = vty_new();
+ vty->type = VTY_TERM;
+
+ fprintf(stderr, "Progress:\n0/%u", vector_active(test_cmds));
+ for (test_cmd = 0; test_cmd < vector_active(test_cmds); test_cmd++) {
+ for (edit_distance = 0; edit_distance <= max_edit_distance;
+ edit_distance++) {
+ num_iterations = 1 << edit_distance;
+ num_iterations *= num_iterations * num_iterations;
+
+ for (iteration = 0; iteration < num_iterations;
+ iteration++)
+ test_run(prng, vty,
+ vector_slot(test_cmds, test_cmd),
+ edit_distance, node_index, verbose);
+ }
+ fprintf(stderr, "\r%u/%u", test_cmd + 1,
+ vector_active(test_cmds));
+ }
+ fprintf(stderr, "\nDone.\n");
+
+ vty_close(vty);
+ prng_free(prng);
+ test_terminate();
+ return 0;
+}