/* * 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 #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"); }