blob: 18b3b50d79f280c2893e7e68d2d8249bd353d4ea [file] [log] [blame]
/*
* 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.
*
* Quagga is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* Quagga is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Quagga; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#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 thread_master *master; /* dummy for libzebra*/
static vector test_cmds;
static char test_buf[32768];
static struct cmd_node bgp_node =
{
BGP_NODE,
"%s(config-router)# ",
};
static struct cmd_node rip_node =
{
RIP_NODE,
"%s(config-router)# ",
};
static struct cmd_node isis_node =
{
ISIS_NODE,
"%s(config-router)# ",
};
static struct cmd_node interface_node =
{
INTERFACE_NODE,
"%s(config-if)# ",
};
static struct cmd_node rmap_node =
{
RMAP_NODE,
"%s(config-route-map)# "
};
static struct cmd_node zebra_node =
{
ZEBRA_NODE,
"%s(config-router)# "
};
static struct cmd_node bgp_vpnv4_node =
{
BGP_VPNV4_NODE,
"%s(config-router-af)# "
};
static struct cmd_node bgp_ipv4_node =
{
BGP_IPV4_NODE,
"%s(config-router-af)# "
};
static struct cmd_node bgp_ipv4m_node =
{
BGP_IPV4M_NODE,
"%s(config-router-af)# "
};
static struct cmd_node bgp_ipv6_node =
{
BGP_IPV6_NODE,
"%s(config-router-af)# "
};
static struct cmd_node bgp_ipv6m_node =
{
BGP_IPV6M_NODE,
"%s(config-router-af)# "
};
static struct cmd_node ospf_node =
{
OSPF_NODE,
"%s(config-router)# "
};
static struct cmd_node ripng_node =
{
RIPNG_NODE,
"%s(config-router)# "
};
static struct cmd_node ospf6_node =
{
OSPF6_NODE,
"%s(config-ospf6)# "
};
static struct cmd_node babel_node =
{
BABEL_NODE,
"%s(config-babel)# "
};
static struct cmd_node keychain_node =
{
KEYCHAIN_NODE,
"%s(config-keychain)# "
};
static struct cmd_node keychain_key_node =
{
KEYCHAIN_KEY_NODE,
"%s(config-keychain-key)# "
};
static int
test_callback(struct cmd_element *cmd, struct vty *vty, int argc, const char *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]);
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_STRVEC, line));
}
}
static void
test_init(void)
{
unsigned int node;
unsigned int i;
struct cmd_node *cnode;
struct cmd_element *cmd;
cmd_init(1);
install_node (&bgp_node, NULL);
install_node (&rip_node, NULL);
install_node (&interface_node, NULL);
install_node (&rmap_node, NULL);
install_node (&zebra_node, NULL);
install_node (&bgp_vpnv4_node, NULL);
install_node (&bgp_ipv4_node, NULL);
install_node (&bgp_ipv4m_node, NULL);
install_node (&bgp_ipv6_node, NULL);
install_node (&bgp_ipv6m_node, NULL);
install_node (&ospf_node, NULL);
install_node (&ripng_node, NULL);
install_node (&ospf6_node, NULL);
install_node (&babel_node, NULL);
install_node (&keychain_node, NULL);
install_node (&keychain_key_node, NULL);
install_node (&isis_node, NULL);
install_node (&vty_node, NULL);
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_STRVEC, vector_slot(test_cmds, i));
vector_free(test_cmds);
cmd_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((int) 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_VECTOR_INDEX, 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 *cmd = vector_slot(descriptions, j);
printf(" '%s' '%s'\n", cmd->cmd, cmd->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;
}