blob: 250379329bc84900473d8baa46d59b232b908cf3 [file] [log] [blame]
/*
* Recursive Nexthop Iterator test.
* This tests the ALL_NEXTHOPS_RO macro.
*
* Copyright (C) 2012 by Open Source Routing.
* Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
*
* This file is part of Quagga
*
* 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.
*/
#include <zebra.h>
#include "zebra/rib.h"
#include "prng.h"
struct thread_master *master;
static int verbose;
static void
str_append(char **buf, const char *repr)
{
if (*buf)
{
*buf = realloc(*buf, strlen(*buf) + strlen(repr) + 1);
assert(*buf);
strncpy((*buf) + strlen(*buf), repr, strlen(repr) + 1);
}
else
{
*buf = strdup(repr);
assert(*buf);
}
}
static void
str_appendf(char **buf, const char *format, ...)
{
va_list ap;
int rv;
char *pbuf;
va_start(ap, format);
rv = vasprintf(&pbuf, format, ap);
va_end(ap);
assert(rv >= 0);
str_append(buf, pbuf);
free(pbuf);
}
/* This structure contains a nexthop chain
* and its expected representation */
struct nexthop_chain
{
/* Head of the chain */
struct nexthop *head;
/* Last nexthop in top chain */
struct nexthop *current_top;
/* Last nexthop in current recursive chain */
struct nexthop *current_recursive;
/* Expected string representation. */
char *repr;
};
static struct nexthop_chain*
nexthop_chain_new(void)
{
struct nexthop_chain *rv;
rv = calloc(sizeof(*rv), 1);
assert(rv);
return rv;
}
static void
nexthop_chain_add_top(struct nexthop_chain *nc)
{
struct nexthop *nh;
nh = calloc(sizeof(*nh), 1);
assert(nh);
if (nc->head)
{
nc->current_top->next = nh;
nh->prev = nc->current_top;
nc->current_top = nh;
}
else
{
nc->head = nc->current_top = nh;
}
nc->current_recursive = NULL;
str_appendf(&nc->repr, "%p\n", nh);
}
static void
nexthop_chain_add_recursive(struct nexthop_chain *nc)
{
struct nexthop *nh;
nh = calloc(sizeof(*nh), 1);
assert(nh);
assert(nc->current_top);
if (nc->current_recursive)
{
nc->current_recursive->next = nh;
nh->prev = nc->current_recursive;
nc->current_recursive = nh;
}
else
{
SET_FLAG(nc->current_top->flags, NEXTHOP_FLAG_RECURSIVE);
nc->current_top->resolved = nh;
nc->current_recursive = nh;
}
str_appendf(&nc->repr, " %p\n", nh);
}
static void
nexthop_chain_clear(struct nexthop_chain *nc)
{
struct nexthop *tcur, *tnext;
for (tcur = nc->head; tcur; tcur = tnext)
{
tnext = tcur->next;
if (CHECK_FLAG(tcur->flags, NEXTHOP_FLAG_RECURSIVE))
{
struct nexthop *rcur, *rnext;
for (rcur = tcur->resolved; rcur; rcur = rnext)
{
rnext = rcur->next;
free(rcur);
}
}
free(tcur);
}
nc->head = nc->current_top = nc->current_recursive = NULL;
free(nc->repr);
nc->repr = NULL;
}
static void
nexthop_chain_free(struct nexthop_chain *nc)
{
if (!nc)
return;
nexthop_chain_clear(nc);
free(nc);
}
/* This function builds a string representation of
* the nexthop chain using the ALL_NEXTHOPS_RO macro.
* It verifies that the ALL_NEXTHOPS_RO macro iterated
* correctly over the nexthop chain by comparing the
* generated representation with the expected representation.
*/
static void
nexthop_chain_verify_iter(struct nexthop_chain *nc)
{
struct nexthop *nh, *tnh;
int recursing;
char *repr = NULL;
for (ALL_NEXTHOPS_RO(nc->head, nh, tnh, recursing))
{
if (recursing)
str_appendf(&repr, " %p\n", nh);
else
str_appendf(&repr, "%p\n", nh);
}
if (repr && verbose)
printf("===\n%s", repr);
assert((!repr && !nc->repr) || (repr && nc->repr && !strcmp(repr, nc->repr)));
free(repr);
}
/* This test run builds a simple nexthop chain
* with some recursive nexthops and verifies that
* the iterator works correctly in each stage along
* the way.
*/
static void
test_run_first(void)
{
struct nexthop_chain *nc;
nc = nexthop_chain_new();
nexthop_chain_verify_iter(nc);
nexthop_chain_add_top(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_top(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_recursive(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_recursive(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_top(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_top(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_top(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_recursive(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_recursive(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_add_recursive(nc);
nexthop_chain_verify_iter(nc);
nexthop_chain_free(nc);
}
/* This test run builds numerous random
* nexthop chain configurations and verifies
* that the iterator correctly progresses
* through each. */
static void
test_run_prng(void)
{
struct nexthop_chain *nc;
struct prng *prng;
int i;
nc = nexthop_chain_new();
prng = prng_new(0);
for (i = 0; i < 1000000; i++)
{
switch (prng_rand(prng) % 10)
{
case 0:
nexthop_chain_clear(nc);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
nexthop_chain_add_top(nc);
break;
case 6:
case 7:
case 8:
case 9:
if (nc->current_top)
nexthop_chain_add_recursive(nc);
break;
}
nexthop_chain_verify_iter(nc);
}
nexthop_chain_free(nc);
prng_free(prng);
}
int main(int argc, char **argv)
{
if (argc >= 2 && !strcmp("-v", argv[1]))
verbose = 1;
test_run_first();
printf("Simple test passed.\n");
test_run_prng();
printf("PRNG test passed.\n");
}