frr/tests/lib/cli/test_commands.c
Igor Ryzhov 5dac696154 lib: rework debug init
The debug library allows to register a `debug_set_all` callback which
should enable all debugs in a daemon. This callback is implemented
exactly the same in each daemon. Instead of duplicating the code, rework
the lib to allow registration of each debug type, and implement the
common code only once in the lib.

Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
2024-08-27 09:53:02 -04:00

404 lines
9.1 KiB
C

// 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 "debug.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);
debug_init();
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;
}