Unit Testing for BIRD

- Unit Testing Framework (BirdTest)
 - Integration of BirdTest into the BIRD build system
 - Tests for several BIRD modules

 Based on squashed Pavel Tvrdik's int-test branch, updated for
 current int-new branch.
This commit is contained in:
Ondrej Zajicek (work) 2016-11-09 16:36:34 +01:00
parent 8860e991f6
commit 9b0a0ba9e6
60 changed files with 6595 additions and 579 deletions

View file

@ -22,7 +22,7 @@ INSTALL_DATA=@INSTALL_DATA@
client=$(addprefix $(exedir)/,@CLIENT@)
daemon=$(exedir)/bird
protocols = @protocols@
protocols=@protocols@
prefix=@prefix@
exec_prefix=@exec_prefix@
@ -49,24 +49,26 @@ else
endif
# Meta rules
cleangoals := clean distclean
docgoals := docs userdocs progdocs
.PHONY: all daemon cli $(cleangoals) $(docgoals) tags
testgoals := check test tests tests_run
cleangoals := clean distclean testsclean
.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags
all: daemon cli
daemon: $(daemon)
cli: $(client)
# Include directories
dirs := client conf doc filter lib nest $(addprefix proto/,$(protocols)) @sysdep_dirs@
dirs := client conf doc filter lib nest test $(addprefix proto/,$(protocols)) @sysdep_dirs@
conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h)
cf-local = $(conf-y-targets): $(s)config.Y
src-o-files = $(patsubst %.c,$(o)%.o,$(src))
tests-target-files = $(patsubst %.c,$(o)%,$(tests_src))
all-daemon = $(exedir)/bird: $(obj)
all-client = $(exedir)/birdc $(exedir)/birdcl: $(obj)
all-daemon = $(daemon): $(obj)
all-client = $(client): $(obj)
s = $(dir $(lastword $(MAKEFILE_LIST)))
ifeq ($(srcdir),.)
@ -109,6 +111,22 @@ $(objdir)/sysdep/paths.h: Makefile
echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\""
if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi
# Unit tests rules
tests_targets_ok = $(addsuffix .ok,$(tests_targets))
$(tests_targets): %: %.o $(tests_objs)
$(E)echo LD $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
$(tests_targets_ok): %.ok: %
$(Q)$* 2>/dev/null && touch $*.ok
test: testsclean check
check: tests tests_run
tests: $(tests_targets)
tests_run: $(tests_targets_ok)
# Finally include the computed dependencies
ifneq ($(filter-out $(cleangoals),$(MAKECMDGOALS)),)
@ -147,6 +165,9 @@ clean::
rm -f $(addprefix $(exedir)/,bird birdc birdcl)
find $(objdir) -name "*.[od]" -exec rm -f '{}' '+'
testsclean:
rm -f $(tests_targets_ok)
ifeq ($(objdir),obj)
distclean: clean
rm -rf $(objdir)

View file

@ -3,6 +3,8 @@ obj := $(src-o-files)
$(all-daemon)
tests_objs := $(tests_objs) $(src-o-files)
ifdef DEBUG
BISON_DEBUG=-t
#FLEX_DEBUG=-d

View file

@ -49,12 +49,10 @@
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "lib/resource.h"
#include "lib/string.h"
#include "lib/event.h"
#include "sysdep/unix/timer.h"
#include "conf/conf.h"
#include "filter/filter.h"
static jmp_buf conf_jmpbuf;
@ -85,7 +83,7 @@ int undo_available; /* Undo was not requested from last reconfiguration */
* further use. Returns a pointer to the structure.
*/
struct config *
config_alloc(const byte *name)
config_alloc(const char *name)
{
pool *p = rp_new(&root_pool, "Config");
linpool *l = lp_new(p, 4080);
@ -96,6 +94,7 @@ config_alloc(const byte *name)
char *ndup = lp_allocu(l, nlen);
memcpy(ndup, name, nlen);
init_list(&c->tests);
c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
c->pool = p;
c->mem = l;

View file

@ -9,6 +9,8 @@
#ifndef _BIRD_CONF_H_
#define _BIRD_CONF_H_
#include "sysdep/config.h"
#include "lib/ip.h"
#include "lib/resource.h"
#include "sysdep/unix/timer.h"
@ -21,6 +23,7 @@ struct config {
list protos; /* Configured protocol instances (struct proto_config) */
list tables; /* Configured routing tables (struct rtable_config) */
list logfiles; /* Configured log files (sysdep) */
list tests; /* Configured unit tests */
int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */
char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
@ -60,7 +63,7 @@ struct config {
extern struct config *config; /* Currently active configuration */
extern struct config *new_config; /* Configuration being parsed */
struct config *config_alloc(const byte *name);
struct config *config_alloc(const char *name);
int config_parse(struct config *);
int cli_parse(struct config *);
void config_free(struct config *);
@ -161,6 +164,7 @@ static inline int cf_symbol_is_constant(struct symbol *sym)
/* Parser */
extern char *cf_text;
int cf_parse(void);
/* Sysdep hooks */

View file

@ -56,7 +56,7 @@ if test "$ac_test_CFLAGS" != set ; then
bird_cflags_default=yes
fi
AC_PROG_CC
AC_PROG_CC_C99
if test -z "$GCC" ; then
AC_MSG_ERROR([This program requires the GNU C Compiler.])
fi
@ -220,6 +220,9 @@ BIRD_CHECK_STRUCT_IP_MREQN
if test "$enable_debug" = yes ; then
AC_DEFINE(DEBUGGING)
AC_CHECK_HEADER(execinfo.h, [AC_SEARCH_LIBS([backtrace, backtrace_symbols], [c execinfo], [AC_DEFINE(HAVE_EXECINFO_H)])])
LDFLAGS="$LDFLAGS -rdynamic"
CFLAGS="$CFLAGS -O0 -ggdb -g3 -gdwarf-4"
if test "$enable_memcheck" = yes ; then
AC_CHECK_LIB(dmalloc, dmalloc_debug)
if test $ac_cv_lib_dmalloc_dmalloc_debug != yes ; then

View file

@ -1253,7 +1253,7 @@ foot).
<cf/!&tilde;/ membership operators) can be used to modify or test
eclists, with ECs instead of pairs as arguments.
<tag/lclist/
<tag><label id="type-lclist">lclist/</tag>
Lclist is a data type used for BGP large community lists. Like eclists,
lclists are very similar to clists, but they are sets of LCs instead of
pairs. The same operations (like <cf/add/, <cf/delete/ or <cf/&tilde;/

View file

@ -2,3 +2,7 @@ src := filter.c f-util.c tree.c trie.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_src := tree_test.c filter_test.c trie_test.c
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -323,7 +323,71 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
return rv;
}
/*
* Remove all new lines and doubled whitespaces
* and convert all tabulators to spaces
* and return a copy of string
*/
char *
assert_copy_expr(const char *start, size_t len)
{
/* XXX: Allocates maybe a little more memory than we really finally need */
char *str = cfg_alloc(len + 1);
char *dst = str;
const char *src = start - 1;
const char *end = start + len;
while (++src < end)
{
if (*src == '\n')
continue;
/* Skip doubled whitespaces */
if (src != start)
{
const char *prev = src - 1;
if ((*src == ' ' || *src == '\t') && (*prev == ' ' || *prev == '\t'))
continue;
}
if (*src == '\t')
*dst = ' ';
else
*dst = *src;
dst++;
}
*dst = '\0';
return str;
}
/*
* assert_done - create f_instruction of bt_assert
* @expr: expression in bt_assert()
* @start: pointer to first char of test expression
* @end: pointer to the last char of test expression
*/
static struct f_inst *
assert_done(struct f_inst *expr, const char *start, const char *end)
{
struct f_inst *i;
i = f_new_inst();
i->code = P('a','s');
i->a1.p = expr;
if (end >= start)
{
i->a2.p = assert_copy_expr(start, end - start + 1);
}
else
{
/* this is a break of lexer buffer */
i->a2.p = "???";
}
return i;
}
CF_DECLS
@ -341,12 +405,13 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ADD, DELETE, CONTAINS, RESET,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
EMPTY,
FILTER, WHERE, EVAL)
FILTER, WHERE, EVAL,
BT_ASSERT, BT_TEST_SUITE)
%nonassoc THEN
%nonassoc ELSE
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr bt_assert
%type <f> filter filter_body where_filter
%type <i> type break_command ec_kind
%type <i32> cnum
@ -356,6 +421,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <px> fprefix
%type <s> decls declsn one_decl function_params
%type <h> bgp_path bgp_path_tail1 bgp_path_tail2
%type <t> get_cf_position
CF_GRAMMAR
@ -375,6 +441,21 @@ filter_eval:
EVAL term { f_eval_int($2); }
;
CF_ADDTO(conf, bt_test_suite)
bt_test_suite:
BT_TEST_SUITE '(' SYM ',' text ')' {
if (!($3->class & SYM_FUNCTION))
cf_error("Function expected");
struct f_bt_test_suite *t = cfg_alloc(sizeof(struct f_bt_test_suite));
t->fn = $3->def;
t->fn_name = $3->name;
t->dsc = $5;
add_tail(&new_config->tests, &t->n);
}
;
type:
INT { $$ = T_INT; }
| BOOL { $$ = T_BOOL; }
@ -835,6 +916,8 @@ term:
| ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
| bt_assert { $$ = $1; }
/* | term '.' LEN { $$->code = P('P','l'); } */
/* function_call is inlined here */
@ -966,6 +1049,7 @@ cmd:
$$->a1.p = $2;
$$->a2.p = build_tree( $4 );
}
| bt_assert ';' { $$ = $1; }
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
@ -975,4 +1059,14 @@ cmd:
| rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( P('C','a'), 'f', $2, $6 ); }
;
bt_assert:
BT_ASSERT '(' get_cf_position term get_cf_position ')' { $$ = assert_done($4, $3 + 1, $5 - 1); }
;
get_cf_position:
{
$$ = cf_text;
};
CF_END

View file

@ -52,6 +52,8 @@
#define CMP_ERROR 999
void (*bt_assert_hook)(int result, struct f_inst *assert);
static struct adata *
adata_empty(struct linpool *pool, int l)
{
@ -563,8 +565,8 @@ f_rta_cow(void)
static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
#define runtime(x) do { \
log_rl(&rl_runtime_err, L_ERR "filters, line %d: %s", what->lineno, x); \
#define runtime(fmt, ...) do { \
log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \
res.type = T_RETURN; \
res.val.i = F_ERROR; \
return res; \
@ -1475,6 +1477,17 @@ interpret(struct f_inst *what)
break;
case P('a', 's'): /* Birdtest Assert */
ONEARG;
if (v1.type != T_BOOL)
runtime("Should be boolean value");
res.type = v1.type;
res.val = v1.val;
CALL(bt_assert_hook, res.val.i, what);
break;
default:
bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff);

View file

@ -16,16 +16,16 @@
struct f_inst { /* Instruction */
struct f_inst *next; /* Structure is 16 bytes, anyway */
u16 code;
u16 aux;
u16 code; /* Instruction code, see the interpret() function and P() macro */
u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
union {
int i;
void *p;
} a1;
} a1; /* The first argument */
union {
int i;
void *p;
} a2;
} a2; /* The second argument */
int lineno;
};
@ -55,7 +55,7 @@ struct f_prefix {
};
struct f_val {
int type;
int type; /* T_* */
union {
uint i;
u64 ec;
@ -205,4 +205,15 @@ struct f_trie
#define FF_FORCE_TMPATTR 1 /* Force all attributes to be temporary */
/* Bird Tests */
struct f_bt_test_suite {
node n; /* Node in config->tests */
struct f_inst *fn; /* Root of function */
const char *fn_name; /* Name of test */
const char *dsc; /* Description */
};
/* Hook for call bt_assert() function in configuration */
extern void (*bt_assert_hook)(int result, struct f_inst *assert);
#endif

87
filter/filter_test.c Normal file
View file

@ -0,0 +1,87 @@
/*
* Filters: Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <string.h>
#include <stdlib.h>
#include "test/birdtest.h"
#include "test/bt-utils.h"
#include "filter/filter.h"
#include "conf/conf.h"
#define BT_CONFIG_FILE "filter/test.conf"
static struct config *
parse_config_file(const void *filename_void)
{
bt_bird_init();
size_t fn_size = strlen((const char *) filename_void) + 1;
char *filename = alloca(fn_size);
strncpy(filename, filename_void, fn_size);
struct config *c = bt_config_file_parse(filename);
bt_bird_cleanup();
return c;
}
static int
run_function(const void *parsed_fn_def)
{
/* XXX: const -> non-const */
struct f_inst *f = (struct f_inst *) parsed_fn_def;
linpool *tmp = lp_new(&root_pool, 4096);
struct f_val res = f_eval(f, tmp);
rfree(tmp);
if (res.type == T_RETURN && res.val.i >= F_REJECT)
return BT_FAILURE;
return BT_SUCCESS;
}
static void
bt_assert_filter(int result, struct f_inst *assert)
{
int bt_suit_case_result = BT_SUCCESS;
if (!result)
{
bt_result = BT_FAILURE;
bt_suite_result = BT_FAILURE;
bt_suit_case_result = BT_FAILURE;
}
bt_log_suite_case_result(bt_suit_case_result, "Assertion at line %d (%s)", assert->lineno, (char *) assert->a2.p);
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
struct config *c = parse_config_file(BT_CONFIG_FILE);
if (c)
{
bt_assert_hook = bt_assert_filter;
struct f_bt_test_suite *t;
WALK_LIST(t, c->tests)
bt_test_suite_base(run_function, t->fn_name, t->fn, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
}
return bt_exit_value();
}

File diff suppressed because it is too large Load diff

View file

@ -2,4 +2,5 @@
print "Entering include";
print "Should be 2: ", 1+1;
print "Leaving include";
i = 42;

View file

@ -18,6 +18,7 @@ protocol direct {
protocol kernel {
disabled;
ipv4; # Must be specified at least one channel
# learn; # Learn all routes from the kernel
# scan time 10; # Scan kernel tables every 10 seconds
}
@ -25,7 +26,11 @@ protocol kernel {
protocol static {
# disabled;
import filter { print "ahoj";
ipv4 {
export all;
import filter {
print "ahoj";
print source;
if source = RTS_STATIC then {
print "It is from static";
@ -49,27 +54,30 @@ protocol static {
print rip_metric;
rip_metric = rip_metric + 5;
print rip_metric;
bgp_community = - empty - ;
print "nazdar";
bgp_community = add(bgp_community, (1,2));
print "cau";
bgp_community = add(bgp_community, (2,3));
bgp_community.add((4,5));
print "community = ", bgp_community;
bgp_community.delete((2,3));
print "community = ", bgp_community;
bgp_community.empty;
print "community = ", bgp_community;
print "done";
};
#
# TODO: uncomment this part after finishing BGP integration version
#
# bgp_community = -empty-;
# print "hi";
# bgp_community = add(bgp_community, (1,2));
# print "hello";
# bgp_community = add(bgp_community, (2,3));
# bgp_community.add((4,5));
# print "community = ", bgp_community;
# bgp_community.delete((2,3));
# print "community = ", bgp_community;
# bgp_community.empty;
# print "community = ", bgp_community;
# print "done";
};
};
route 0.0.0.0/0 via 195.113.31.113;
route 62.168.0.0/25 reject;
route 1.2.3.4/32 via 195.113.31.124;
# route 10.0.0.0/8 reject;
# route 10.1.1.0:255.255.255.0 via 62.168.0.3;
# route 10.1.2.0:255.255.255.0 via 62.168.0.3;
# route 10.1.3.0:255.255.255.0 via 62.168.0.4;
# route 10.2.0.0/24 via "arc0";
export all;
route 10.0.0.0/8 reject;
route 10.1.1.0:255.255.255.0 via 62.168.0.3;
route 10.1.2.0:255.255.255.0 via 62.168.0.3;
route 10.1.3.0:255.255.255.0 via 62.168.0.4;
route 10.2.0.0/24 via "arc0";
}

View file

@ -9,6 +9,8 @@ router id 62.168.0.1;
define xyzzy = (120+10);
protocol device {}
function callme(int arg1; int arg2)
int local1;
int local2;
@ -30,7 +32,7 @@ function fifteen()
return 15;
}
function paths()
function _paths()
bgpmask pm1;
bgpmask pm2;
bgppath p2;
@ -97,7 +99,7 @@ ip p;
pair pp;
int set is;
prefix set pxs;
string s;
string str;
{
print "Testing filter language:";
i = four;
@ -129,8 +131,8 @@ string s;
print "Testing pairs: (1,2) = ", (1,2), " = ", pp;
print "Testing enums: ", RTS_DUMMY, " ", RTS_STATIC;
s = "Hello";
print "Testing string: ", s, " true: ", s ~ "Hell*", " false: ", s ~ "ell*";
str = "Hello";
print "Testing string: ", str, " true: ", str ~ "Hell*", " false: ", str ~ "ell*";
b = true;
print "Testing bool: ", b, ", ", !b;
@ -156,11 +158,12 @@ string s;
i = fifteen();
print "Testing function calls: 15 = ", i;
paths();
_paths();
print "done";
quitbird;
# print "*** FAIL: this is unreachable";
return 0;
print "*** FAIL: this is unreachable";
quitbird; # quit with err exit code 1
}
filter testf

View file

@ -0,0 +1,113 @@
router id 62.168.0.1;
function net_martian()
{
return net ~ [ 169.254.0.0/16+, 172.16.0.0/12+, 192.168.0.0/16+, 10.0.0.0/8+,
127.0.0.0/8+, 224.0.0.0/4+, 240.0.0.0/4+, 0.0.0.0/32-, 0.0.0.0/0{25,32}, 0.0.0.0/0{0,7} ];
}
function net_local()
{
return net ~ [ 12.10.0.0/16+, 34.10.0.0/16+ ];
}
function rt_import(int asn; int set peer_asns; prefix set peer_nets)
{
if ! (net ~ peer_nets) then return false;
if ! (bgp_path.last ~ peer_asns) then return false;
if bgp_path.first != asn then return false;
if bgp_path.len > 64 then return false;
if bgp_next_hop != from then return false;
return true;
}
function rt_import_all(int asn)
{
if net_martian() || net_local() then return false;
if bgp_path.first != asn then return false;
if bgp_path.len > 64 then return false;
if bgp_next_hop != from then return false;
return true;
}
function rt_import_rs(int asn)
{
if net_martian() || net_local() then return false;
if bgp_path.len > 64 then return false;
return true;
}
function rt_export()
{
if proto = "static_bgp" then return true;
if source != RTS_BGP then return false;
if net_martian() then return false;
if bgp_path.len > 64 then return false;
# return bgp_next_hop ~ [ 100.1.1.1, 100.1.1.2, 200.1.1.1 ];
return bgp_path.first ~ [ 345, 346 ];
}
function rt_export_all()
{
if proto = "static_bgp" then return true;
if source != RTS_BGP then return false;
if net_martian() then return false;
if bgp_path.len > 64 then return false;
return true;
}
filter bgp_in_uplink_123
{
if ! rt_import_all(123) then reject;
accept;
}
filter bgp_out_uplink_123
{
if ! rt_export() then reject;
accept;
}
filter bgp_in_peer_234
{
if ! rt_import(234, [ 234, 1234, 2345, 3456 ],
[ 12.34.0.0/16, 23.34.0.0/16, 34.56.0.0/16 ])
then reject;
accept;
}
filter bgp_out_peer_234
{
if ! rt_export() then reject;
accept;
}
filter bgp_in_rs
{
if ! rt_import_rs(bgp_path.last) then reject;
accept;
}
filter bgp_out_rs
{
if ! rt_export() then reject;
accept;
}
filter bgp_in_client_345
{
if ! rt_import(345, [ 345 ], [ 34.5.0.0/16 ]) then reject;
accept;
}
filter bgp_out_client_345
{
if ! rt_export_all() then reject;
accept;
}

304
filter/tree_test.c Normal file
View file

@ -0,0 +1,304 @@
/*
* Filters: Utility Functions Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "test/bt-utils.h"
#include "filter/filter.h"
#include "conf/conf.h"
#define MAX_TREE_HEIGHT 13
static void
start_conf_env(void)
{
bt_bird_init();
pool *p = rp_new(&root_pool, "helper_pool");
linpool *l = lp_new(p, 4080);
cfg_mem = l;
}
static struct f_tree *
new_tree(uint id)
{
struct f_tree *tree = f_new_tree();
tree->from.type = tree->to.type = T_INT;
tree->from.val.i = tree->to.val.i = id;
return tree;
}
/*
* Show subtree in infix notation
*/
static void
show_subtree(struct f_tree *node)
{
if (!node)
return;
show_subtree(node->left);
if (node->from.val.i == node->to.val.i)
bt_debug("%u ", node->from.val.i);
else
bt_debug("%u..%u ", node->from.val.i, node->to.val.i);
show_subtree(node->right);
}
static void
show_tree2(struct f_tree *root_node, const char *tree_name)
{
bt_debug("%s: \n", tree_name);
bt_debug("[ ");
show_subtree(root_node);
bt_debug("]\n\n");
}
#define show_tree(tree) show_tree2(tree, #tree);
static uint
get_nodes_count_full_bin_tree(uint height)
{
return (bt_naive_pow(2, height+1) - 1);
}
static struct f_tree *
get_balanced_full_subtree(uint height, uint idx)
{
struct f_tree *node = new_tree(idx);
if (height > 0)
{
uint nodes_in_subtree = get_nodes_count_full_bin_tree(--height);
node->left = get_balanced_full_subtree(height, idx - nodes_in_subtree/2 - 1);
node->right = get_balanced_full_subtree(height, idx + nodes_in_subtree/2 + 1);
}
return node;
}
static struct f_tree *
get_balanced_full_tree(uint height)
{
return get_balanced_full_subtree(height, get_nodes_count_full_bin_tree(height)/2);
}
static struct f_tree *
get_degenerated_left_tree(uint nodes_count)
{
struct f_tree *old = NULL;
struct f_tree *new = NULL;
uint i;
for (i = 0; i < nodes_count; i++)
{
old = new;
new = new_tree(nodes_count-1-i);
new->left = old;
}
return new;
}
static struct f_tree *
get_random_degenerated_left_tree(uint nodes_count)
{
struct f_tree *tree = get_degenerated_left_tree(nodes_count);
size_t avaible_indexes_size = nodes_count * sizeof(byte);
byte *avaible_indexes = malloc(avaible_indexes_size);
memset(avaible_indexes, 0, avaible_indexes_size);
struct f_tree *n;
for (n = tree; n; n = n->left)
{
uint selected_idx;
do
{
selected_idx = bt_random() % nodes_count;
} while(avaible_indexes[selected_idx] != 0);
avaible_indexes[selected_idx] = 1;
n->from.type = n->to.type = T_INT;
n->from.val.i = n->to.val.i = selected_idx;
}
free(avaible_indexes);
return tree;
}
static struct f_tree *
get_balanced_tree_with_ranged_values(uint nodes_count)
{
struct f_tree *tree = get_degenerated_left_tree(nodes_count);
uint idx = 0;
struct f_tree *n;
for (n = tree; n; n = n->left)
{
n->from.type = n->to.type = T_INT;
n->from.val.i = idx;
idx += (uint)bt_random() / nodes_count; /* (... / nodes_count) preventing overflow an uint idx */
n->to.val.i = idx++;
}
return build_tree(tree);
}
static int
t_balancing(void)
{
start_conf_env();
uint height;
for (height = 1; height < MAX_TREE_HEIGHT; height++)
{
uint nodes_count = get_nodes_count_full_bin_tree(height);
struct f_tree *simple_degenerated_tree = get_degenerated_left_tree(nodes_count);
show_tree(simple_degenerated_tree);
struct f_tree *expected_balanced_tree = get_balanced_full_tree(height);
show_tree(expected_balanced_tree);
struct f_tree *balanced_tree_from_simple = build_tree(simple_degenerated_tree);
show_tree(balanced_tree_from_simple);
bt_assert(same_tree(balanced_tree_from_simple, expected_balanced_tree));
}
return BT_SUCCESS;
}
static int
t_balancing_random(void)
{
start_conf_env();
uint height;
for (height = 1; height < MAX_TREE_HEIGHT; height++)
{
uint nodes_count = get_nodes_count_full_bin_tree(height);
struct f_tree *expected_balanced_tree = get_balanced_full_tree(height);
uint i;
for(i = 0; i < 10; i++)
{
struct f_tree *random_degenerated_tree = get_random_degenerated_left_tree(nodes_count);
show_tree(random_degenerated_tree);
struct f_tree *balanced_tree_from_random = build_tree(random_degenerated_tree);
show_tree(expected_balanced_tree);
show_tree(balanced_tree_from_random);
bt_assert(same_tree(balanced_tree_from_random, expected_balanced_tree));
}
}
return BT_SUCCESS;
}
static int
t_find(void)
{
start_conf_env();
uint height;
for (height = 1; height < MAX_TREE_HEIGHT; height++)
{
uint nodes_count = get_nodes_count_full_bin_tree(height);
struct f_tree *tree = get_balanced_full_tree(height);
show_tree(tree);
struct f_val looking_up_value = {
.type = T_INT
};
for(looking_up_value.val.i = 0; looking_up_value.val.i < nodes_count; looking_up_value.val.i++)
{
struct f_tree *found_tree = find_tree(tree, looking_up_value);
bt_assert((val_compare(looking_up_value, found_tree->from) == 0) && (val_compare(looking_up_value, found_tree->to) == 0));
}
}
return BT_SUCCESS;
}
static uint
get_max_value_in_unbalanced_tree(struct f_tree *node, uint max)
{
if (!node)
return max;
if (node->to.val.i > max)
max = node->to.val.i;
uint max_left = get_max_value_in_unbalanced_tree(node->left, max);
if (max_left > max)
max = max_left;
uint max_right = get_max_value_in_unbalanced_tree(node->right, max);
if (max_right > max)
max = max_right;
return max;
}
static int
t_find_ranges(void)
{
start_conf_env();
uint height;
for (height = 1; height < MAX_TREE_HEIGHT; height++)
{
uint nodes_count = get_nodes_count_full_bin_tree(height);
struct f_tree *tree = get_balanced_tree_with_ranged_values(nodes_count);
uint max_value = get_max_value_in_unbalanced_tree(tree, 0);
show_tree(tree);
bt_debug("max_value: %u \n", max_value);
struct f_val needle = {
.type = T_INT
};
uint *i = &needle.val.i;
for(*i = 0; *i <= max_value; *i += (uint)bt_random()/nodes_count)
{
struct f_tree *found_tree = find_tree(tree, needle);
bt_debug("searching: %u \n", *i);
bt_assert(
(val_compare(needle, found_tree->from) == 0) || (val_compare(needle, found_tree->to) == 0) ||
((val_compare(needle, found_tree->from) == 1) && (val_compare(needle, found_tree->to) == -1))
);
}
}
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_balancing, "Balancing strong unbalanced trees");
bt_test_suite(t_balancing_random, "Balancing random unbalanced trees");
bt_test_suite(t_find, "Finding values in trees");
bt_test_suite(t_find_ranges, "Finding values in trees with random ranged values");
return bt_exit_value();
}

184
filter/trie_test.c Normal file
View file

@ -0,0 +1,184 @@
/*
* Filters: Utility Functions Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "test/bt-utils.h"
#include "filter/filter.h"
#include "conf/conf.h"
#define TESTS_NUM 10
#define PREFIXES_NUM 10
#define PREFIX_TESTS_NUM 10000
#define BIG_BUFFER_SIZE 10000
/* Wrapping structure for storing f_prefixes structures in list */
struct f_prefix_node {
node n;
struct f_prefix prefix;
};
static u32
xrandom(u32 max)
{
return (bt_random() % max);
}
static int
is_prefix_included(list *prefixes, struct f_prefix *needle)
{
struct f_prefix_node *n;
WALK_LIST(n, *prefixes)
{
ip6_addr cmask = ip6_mkmask(MIN(n->prefix.net.pxlen, needle->net.pxlen));
ip6_addr ip = net6_prefix(&n->prefix.net);
ip6_addr needle_ip = net6_prefix(&needle->net);
if ((ipa_compare(ipa_and(ip, cmask), ipa_and(needle_ip, cmask)) == 0) &&
(n->prefix.lo <= needle->net.pxlen) && (needle->net.pxlen <= n->prefix.hi))
{
bt_debug("FOUND\t" PRIip6 "/%d %d-%d\n", ARGip6(net6_prefix(&n->prefix.net)), n->prefix.net.pxlen, n->prefix.lo, n->prefix.hi);
return 1; /* OK */
}
}
return 0; /* FAIL */
}
static struct f_prefix
get_random_ip6_prefix(void)
{
struct f_prefix p;
u8 pxlen = xrandom(120)+8;
ip6_addr ip6 = ip6_build(bt_random(),bt_random(),bt_random(),bt_random());
net_addr_ip6 net6 = NET_ADDR_IP6(ip6, pxlen);
p.net = *((net_addr*) &net6);
if (bt_random() % 2)
{
p.lo = 0;
p.hi = p.net.pxlen;
}
else
{
p.lo = p.net.pxlen;
p.hi = net_max_prefix_length[p.net.type];
}
return p;
}
static void
generate_random_ipv6_prefixes(list *prefixes)
{
int i;
for (i = 0; i < PREFIXES_NUM; i++)
{
struct f_prefix f = get_random_ip6_prefix();
struct f_prefix_node *px = calloc(1, sizeof(struct f_prefix_node));
px->prefix = f;
bt_debug("ADD\t" PRIip6 "/%d %d-%d\n", ARGip6(net6_prefix(&px->prefix.net)), px->prefix.net.pxlen, px->prefix.lo, px->prefix.hi);
add_tail(prefixes, &px->n);
}
}
static int
t_match_net(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
uint round;
for (round = 0; round < TESTS_NUM; round++)
{
list prefixes; /* of structs f_extended_prefix */
init_list(&prefixes);
struct f_trie *trie = f_new_trie(config->mem, sizeof(struct f_trie_node));
generate_random_ipv6_prefixes(&prefixes);
struct f_prefix_node *n;
WALK_LIST(n, prefixes)
{
trie_add_prefix(trie, &n->prefix.net, n->prefix.lo, n->prefix.hi);
}
int i;
for (i = 0; i < PREFIX_TESTS_NUM; i++)
{
struct f_prefix f = get_random_ip6_prefix();
bt_debug("TEST\t" PRIip6 "/%d\n", ARGip6(net6_prefix(&f.net)), f.net.pxlen);
int should_be = is_prefix_included(&prefixes, &f);
int is_there = trie_match_net(trie, &f.net);
bt_assert_msg(should_be == is_there, "Prefix " PRIip6 "/%d %s", ARGip6(net6_prefix(&f.net)), f.net.pxlen, (should_be ? "should be found in trie" : "should not be found in trie"));
}
struct f_prefix_node *nxt;
WALK_LIST_DELSAFE(n, nxt, prefixes)
{
free(n);
}
}
return BT_SUCCESS;
}
static int
t_trie_same(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
int round;
for (round = 0; round < TESTS_NUM*4; round++)
{
struct f_trie * trie1 = f_new_trie(config->mem, sizeof(struct f_trie_node));
struct f_trie * trie2 = f_new_trie(config->mem, sizeof(struct f_trie_node));
list prefixes; /* a list of f_extended_prefix structures */
init_list(&prefixes);
int i;
for (i = 0; i < 100; i++)
generate_random_ipv6_prefixes(&prefixes);
struct f_prefix_node *n;
WALK_LIST(n, prefixes)
{
trie_add_prefix(trie1, &n->prefix.net, n->prefix.lo, n->prefix.hi);
}
WALK_LIST_BACKWARDS(n, prefixes)
{
trie_add_prefix(trie2, &n->prefix.net, n->prefix.lo, n->prefix.hi);
}
bt_assert(trie_same(trie1, trie2));
struct f_prefix_node *nxt;
WALK_LIST_DELSAFE(n, nxt, prefixes)
{
free(n);
}
}
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_match_net, "Testing random prefix matching");
bt_test_suite(t_trie_same, "A trie filled forward should be same with a trie filled backward.");
return bt_exit_value();
}

View file

@ -1,3 +1,7 @@
src := bitops.c checksum.c event.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c xmalloc.c
obj := $(src-o-files)
$(all-daemon)
tests_src := heap_test.c buffer_test.c event_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -56,7 +56,6 @@ static inline int u64_cmp(u64 i1, u64 i2)
#define NULL ((void *) 0)
#endif
/* Macros for gcc attributes */
#define NORET __attribute__((noreturn))

View file

@ -9,6 +9,8 @@
#ifndef _BIRD_BITOPTS_H_
#define _BIRD_BITOPTS_H_
#include "sysdep/config.h"
/*
* Bit mask operations:
*

123
lib/bitops_test.c Normal file
View file

@ -0,0 +1,123 @@
/*
* BIRD Library -- Generic Bit Operations Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "test/bt-utils.h" /* naive_pow() */
#include "lib/bitops.h"
#define MAX_NUM 1000
#define CHECK_BIT(var,pos) ((var) & (u32)(1<<(pos)))
static int
t_mkmask(void)
{
int i;
u32 compute, expect;
bt_assert(u32_mkmask(0) == 0x00000000);
for (i = 1; i <= 32; i++)
{
compute = u32_mkmask(i);
expect = (u32) (0xffffffff << (32-i));
bt_assert_msg(compute == expect, "u32_mkmask(%d) = 0x%08X, expected 0x%08X", i, compute, expect);
}
return BT_SUCCESS;
}
static int
u32_masklen_expected(u32 mask)
{
int j, expect = 0;
int valid = 0;
for (j = 0; j <= 32; j++)
if (mask == (j ? (0xffffffff << (32-j)) : 0)) /* Shifting 32-bit value by 32 bits is undefined behavior */
valid = 1;
if (!valid && mask != 0)
expect = 255;
else
for (j = 0; j <= 31; j++)
if (CHECK_BIT(mask, (31-j)))
expect = j+1;
else
break;
return expect;
}
static void
check_mask(u32 mask)
{
int expected, masklen;
expected = u32_masklen_expected(mask);
masklen = u32_masklen(mask);
int ok = (expected == masklen);
bt_debug("u32_masklen(Ox%08x) = %d, expected %d %s\n", mask, masklen, expected, ok ? "OK" : "FAIL!");
bt_assert(ok);
}
static int
t_masklen(void)
{
u32 i;
check_mask(0x82828282);
check_mask(0x00000000);
for (i = 0; i <= 32; i++)
check_mask(((u32) (i ? (0xffffffff << (32-i)) : 0)) & 0xffffffff); /* Shifting 32-bit value by 32 bits is undefined behavior */
for (i = 0; i <= MAX_NUM; i++)
check_mask(bt_random());
return BT_SUCCESS;
}
static void
check_log2(u32 n)
{
u32 log = u32_log2(n);
u32 low = bt_naive_pow(2, log);
u32 high = bt_naive_pow(2, log+1);
bt_assert_msg(n >= low && n < high,
"u32_log2(%u) = %u, %u should be in the range <%u, %u)",
n, log, n, low, high);
}
static int
t_log2(void)
{
u32 i;
for (i = 0; i < 31; i++)
bt_assert(u32_log2(bt_naive_pow(2, i+1)) == i+1);
for (i = 1; i < MAX_NUM; i++)
check_log2(i);
for (i = 1; i < MAX_NUM; i++)
check_log2(((u32) bt_random()) % 0x0fffffff);
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_mkmask, "u32_mkmask()");
bt_test_suite(t_masklen, "u32_masklen()");
bt_test_suite(t_log2, "u32_log2()");
return bt_exit_value();
}

147
lib/buffer_test.c Normal file
View file

@ -0,0 +1,147 @@
/*
* BIRD Library -- Buffer Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include "test/birdtest.h"
#include "lib/buffer.h"
#define MAX_NUM 33
typedef BUFFER(int) buffer_int;
static int expected[MAX_NUM];
static buffer_int buf;
static struct pool *buffer_pool;
static void
show_buf(buffer_int *b)
{
uint i;
bt_debug(".used = %d, .size = %d\n", b->used, b->size);
for (i = 0; i < b->used; i++)
bt_debug(" .data[%3u] = %-16d expected %-16d %s\n", i, b->data[i], expected[i], (b->data[i] == expected[i] ? "OK" : "FAIL!"));
}
static void
fill_expected_array(void)
{
int i;
for (i = 0; i < MAX_NUM; i++)
expected[i] = bt_random();
}
static void
init_buffer(void)
{
resource_init();
buffer_pool = &root_pool;
BUFFER_INIT(buf, buffer_pool, MAX_NUM);
}
static int
is_buffer_as_expected(buffer_int *b)
{
show_buf(b);
int i;
for (i = 0; i < MAX_NUM; i++)
bt_assert(b->data[i] == expected[i]);
return 1;
}
static int
t_buffer_push(void)
{
int i;
init_buffer();
fill_expected_array();
for (i = 0; i < MAX_NUM; i++)
BUFFER_PUSH(buf) = expected[i];
is_buffer_as_expected(&buf);
return BT_SUCCESS;
}
static int
t_buffer_pop(void)
{
int i;
init_buffer();
fill_expected_array();
/* POP a half of elements */
for (i = 0; i < MAX_NUM; i++)
BUFFER_PUSH(buf) = expected[i];
for (i = MAX_NUM-1; i >= MAX_NUM/2; i--)
BUFFER_POP(buf);
for (i = MAX_NUM/2; i < MAX_NUM; i++)
BUFFER_PUSH(buf) = expected[i] = bt_random();
is_buffer_as_expected(&buf);
/* POP all of elements */
for (i = MAX_NUM-1; i >= 0; i--)
BUFFER_POP(buf);
bt_assert(buf.used == 0);
for (i = 0; i < MAX_NUM; i++)
BUFFER_PUSH(buf) = expected[i];
is_buffer_as_expected(&buf);
return BT_SUCCESS;
}
static int
t_buffer_resize(void)
{
int i;
init_buffer();
BUFFER_INIT(buf, buffer_pool, 0);
fill_expected_array();
for (i = 0; i < MAX_NUM; i++)
BUFFER_PUSH(buf) = expected[i];
is_buffer_as_expected(&buf);
bt_assert(buf.size >= MAX_NUM);
return BT_SUCCESS;
}
static int
t_buffer_flush(void)
{
int i;
init_buffer();
fill_expected_array();
for (i = 0; i < MAX_NUM; i++)
BUFFER_PUSH(buf) = expected[i];
BUFFER_FLUSH(buf);
bt_assert(buf.used == 0);
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_buffer_push, "Pushing new elements");
bt_test_suite(t_buffer_pop, "Fill whole buffer (PUSH), a half of elements POP and PUSH new elements");
bt_test_suite(t_buffer_resize, "Init a small buffer and try overfill");
bt_test_suite(t_buffer_flush, "Fill and flush all elements");
return bt_exit_value();
}

94
lib/checksum_test.c Normal file
View file

@ -0,0 +1,94 @@
/*
* BIRD Library -- IP One-Complement Checksum Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdio.h>
#include "test/birdtest.h"
#include "lib/checksum.h"
#define MAX_NUM 10000
static u16
ipsum_calculate_expected(u32 *a)
{
int i;
u32 sum = 0;
for(i = 0; i < MAX_NUM; i++)
{
sum += a[i] & 0xffff;
bt_debug("low) \t0x%08X \n", sum);
sum += a[i] >> 16;
bt_debug("high) \t0x%08X \n", sum);
u16 carry = sum >> 16;
sum = (sum & 0xffff) + carry;
bt_debug("carry) \t0x%08X \n\n", sum);
}
bt_debug("sum) \t0x%08X \n", sum);
sum = sum ^ 0xffff;
bt_debug("~sum) \t0x%08X \n", sum);
return sum;
}
static int
t_calculate(void)
{
u32 a[MAX_NUM];
int i;
for (i = 0; i < MAX_NUM; i++)
a[i] = bt_random();
u16 sum_calculated = ipsum_calculate(a, sizeof(a), NULL);
u16 sum_calculated_2 = ipsum_calculate(&a[0], sizeof(u32)*(MAX_NUM/2), &a[MAX_NUM/2], sizeof(u32)*(MAX_NUM - MAX_NUM/2), NULL);
bt_assert(sum_calculated == sum_calculated_2);
u16 sum_expected = ipsum_calculate_expected(a);
bt_debug("sum_calculated: %08X \n", sum_calculated);
bt_debug("sum_expected: %08X \n", sum_expected);
bt_assert(sum_calculated == sum_expected);
return BT_SUCCESS;
}
static int
t_verify(void)
{
u32 a[MAX_NUM+1];
int i;
for (i = 0; i < MAX_NUM; i++)
a[i] = bt_random();
u16 sum = ipsum_calculate_expected(a);
a[MAX_NUM] = sum;
bt_assert(ipsum_verify(a, sizeof(a), NULL));
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_calculate, "Checksum of pseudo-random data");
bt_test_suite(t_verify, "Verification of pseudo-random data.");
return bt_exit_value();
}

88
lib/event_test.c Normal file
View file

@ -0,0 +1,88 @@
/*
* BIRD Library -- Event Processing Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/net.h"
#include "lib/event.h"
#include "conf/conf.h"
#include "nest/locks.h"
#include "sysdep/unix/unix.h"
#include "nest/iface.h"
#include "nest/route.h"
#define MAX_NUM 4
int event_check_points[MAX_NUM];
#define event_hook_body(num) \
do { \
bt_debug("Event Hook " #num "\n"); \
event_check_points[num] = 1; \
bt_assert_msg(event_check_points[num-1], "Events should be run in right order"); \
} while (0)
static void event_hook_1(void *data UNUSED) { event_hook_body(1); }
static void event_hook_2(void *data UNUSED) { event_hook_body(2); }
static void event_hook_3(void *data UNUSED) { event_hook_body(3); }
#define schedule_event(num) \
do { \
struct event *event_##num = ev_new(&root_pool); \
event_##num->hook = event_hook_##num; \
ev_schedule(event_##num); \
} while (0)
static void
init_event_check_points(void)
{
int i;
event_check_points[0] = 1;
for (i = 1; i < MAX_NUM; i++)
event_check_points[i] = 0;
}
static int
t_ev_run_list(void)
{
int i;
resource_init();
olock_init();
io_init();
rt_init();
if_init();
// roa_init();
config_init();
config = config_alloc("");
init_event_check_points();
schedule_event(1);
schedule_event(2);
schedule_event(3);
ev_run_list(&global_event_list);
for (i = 1; i < MAX_NUM; i++)
bt_assert(event_check_points[i]);
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_ev_run_list, "Schedule and run 3 events in right order.");
return bt_exit_value();
}

169
lib/fletcher16_test.c Normal file
View file

@ -0,0 +1,169 @@
/*
* BIRD Library -- Fletcher-16 Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/fletcher16.h"
static u16
straightforward_fletcher16_compute(const char *data)
{
int count = strlen(data);
u16 sum1 = 0;
u16 sum2 = 0;
int index;
for (index = 0; index < count; ++index)
{
sum1 = (sum1 + data[index]) % 255;
sum2 = (sum2 + sum1) % 255;
}
return (sum2 << 8) | sum1;
}
static u16
straightforward_fletcher16_checksum(const char *data)
{
u16 csum;
u8 c0,c1,f0,f1;
csum = straightforward_fletcher16_compute(data);
f0 = csum & 0xff;
f1 = (csum >> 8) & 0xff;
c0 = 0xff - ((f0 + f1) % 0xff);
c1 = 0xff - ((f0 + c0) % 0xff);
return (c1 << 8) | c0;
}
static int
test_fletcher16(void *out_, const void *in_, const void *expected_out_)
{
u16 *out = out_;
const char *in = in_;
const u16 *expected_out = expected_out_;
struct fletcher16_context ctxt;
fletcher16_init(&ctxt);
fletcher16_update(&ctxt, in, strlen(in));
put_u16(out, fletcher16_compute(&ctxt));
return (*out == *expected_out) ? BT_SUCCESS : BT_FAILURE;
}
static int
test_fletcher16_checksum(void *out_, const void *in_, const void *expected_out_)
{
u16 *out = out_;
const char *in = in_;
const u16 *expected_out = expected_out_;
struct fletcher16_context ctxt;
int len = strlen(in);
fletcher16_init(&ctxt);
fletcher16_update(&ctxt, in, len);
put_u16(out, fletcher16_final(&ctxt, len, len));
return (*out == *expected_out) ? BT_SUCCESS : BT_FAILURE;
}
static int
t_fletcher16_compute(void)
{
struct bt_pair test_vectors[] = {
{
.in = "\001\002",
.out = & (const u16) { 0x0403 },
},
{
.in = "",
.out = & ((const u16) { straightforward_fletcher16_compute("") }),
},
{
.in = "a",
.out = & ((const u16) { straightforward_fletcher16_compute("a") }),
},
{
.in = "abcd",
.out = & ((const u16) { straightforward_fletcher16_compute("abcd") }),
},
{
.in = "message digest",
.out = & ((const u16) { straightforward_fletcher16_compute("message digest") }),
},
{
.in = "abcdefghijklmnopqrstuvwxyz",
.out = & ((const u16) { straightforward_fletcher16_compute("abcdefghijklmnopqrstuvwxyz") }),
},
{
.in = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
.out = & ((const u16) { straightforward_fletcher16_compute("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") }),
},
{
.in = "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
.out = & ((const u16) { straightforward_fletcher16_compute("12345678901234567890123456789012345678901234567890123456789012345678901234567890") }),
},
};
return bt_assert_batch(test_vectors, test_fletcher16, bt_fmt_str, bt_fmt_unsigned);
}
static int
t_fletcher16_checksum(void)
{
struct bt_pair test_vectors[] = {
{
.in = "\001\002",
.out = & ((const u16) { straightforward_fletcher16_checksum("\001\002") }),
},
{
.in = "",
.out = & ((const u16) { straightforward_fletcher16_checksum("") }),
},
{
.in = "a",
.out = & ((const u16) { straightforward_fletcher16_checksum("a") }),
},
{
.in = "abcd",
.out = & ((const u16) { straightforward_fletcher16_checksum("abcd") }),
},
{
.in = "message digest",
.out = & ((const u16) { straightforward_fletcher16_checksum("message digest") }),
},
{
.in = "abcdefghijklmnopqrstuvwxyz",
.out = & ((const u16) { straightforward_fletcher16_checksum("abcdefghijklmnopqrstuvwxyz") }),
},
{
.in = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
.out = & ((const u16) { straightforward_fletcher16_checksum("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") }),
},
{
.in = "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
.out = & ((const u16) { straightforward_fletcher16_checksum("12345678901234567890123456789012345678901234567890123456789012345678901234567890") }),
},
};
return bt_assert_batch(test_vectors, test_fletcher16_checksum, bt_fmt_str, bt_fmt_unsigned);
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_fletcher16_compute, "Fletcher-16 Compute Tests");
bt_test_suite(t_fletcher16_checksum, "Fletcher-16 Checksum Tests");
return bt_exit_value();
}

305
lib/hash_test.c Normal file
View file

@ -0,0 +1,305 @@
/*
* BIRD Library -- Hash Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#undef LOCAL_DEBUG
#include "test/birdtest.h"
#include "lib/hash.h"
struct test_node {
struct test_node *next; /* Hash chain */
u32 key;
};
#define TEST_KEY(n) n->key
#define TEST_NEXT(n) n->next
#define TEST_EQ(n1,n2) n1 == n2
#define TEST_FN(n) (n) ^ u32_hash((n))
#define TEST_ORDER 13
#define TEST_PARAMS /TEST_ORDER, *2, 2, 2, TEST_ORDER, 20
#define TEST_REHASH test_rehash
HASH_DEFINE_REHASH_FN(TEST, struct test_node);
HASH(struct test_node) hash;
struct pool *my_pool;
#define MAX_NUM (1 << TEST_ORDER)
struct test_node nodes[MAX_NUM];
static void
print_rate_of_fulfilment(void)
{
int i;
int num_stacked_items = 0;
for (i = 0; i < MAX_NUM; i++)
if (!hash.data[i])
num_stacked_items++;
double percent_stacked_items = ((double)num_stacked_items/(double)MAX_NUM)*100.;
bt_debug("%d (%.2f %%) chained of %d hashes \n", num_stacked_items, percent_stacked_items, MAX_NUM);
}
#ifdef LOCAL_DEBUG
static void
dump_nodes(void)
{
int i;
for (i = 0; i < MAX_NUM; i++)
bt_debug("nodes[%3d] is at address %14p has .key %3d, .next %14p \n", i, &nodes[i], nodes[i].key, nodes[i].next);
}
#endif
static void
init_hash_(uint order)
{
resource_init();
my_pool = rp_new(&root_pool, "Test pool");
HASH_INIT(hash, my_pool, order);
int i;
for (i = 0; i < MAX_NUM; i++)
{
nodes[i].key = i;
nodes[i].next = NULL;
}
bt_debug("MAX_NUM %d \n", MAX_NUM);
}
static void
init_hash(void)
{
init_hash_(TEST_ORDER);
}
static void
validate_filled_hash(void)
{
int i;
struct test_node *node;
for (i = 0; i < MAX_NUM; i++)
{
node = HASH_FIND(hash, TEST, nodes[i].key);
bt_assert_msg(node->key == nodes[i].key, "Hash should be filled, to find (%p) the node[%d] (%p) with .key = %u, .next %p", node, i, &nodes[i], nodes[i].key, nodes[i].next);
}
print_rate_of_fulfilment();
}
static void
validate_empty_hash(void)
{
int i;
struct test_node *node;
for (i = 0; i < MAX_NUM; i++)
{
node = HASH_FIND(hash, TEST, nodes[i].key);
bt_assert_msg(node == NULL, "Hash should be empty, to find (%p) the node[%d] (%p) with .key %u, .next %p", node, i, &nodes[i], nodes[i].key, nodes[i].next);
}
}
static void
fill_hash(void)
{
int i;
struct test_node *node;
for (i = 0; i < MAX_NUM; i++)
{
nodes[i].key = i;
node = &nodes[i];
HASH_INSERT(hash, TEST, node);
}
}
static int
t_insert_find(void)
{
init_hash();
fill_hash();
validate_filled_hash();
return BT_SUCCESS;
}
static int
t_insert_find_random(void)
{
init_hash();
int i;
struct test_node *node;
for (i = 0; i < MAX_NUM; i++)
{
nodes[i].key = bt_random();
node = &nodes[i];
HASH_INSERT(hash, TEST, node);
}
validate_filled_hash();
return BT_SUCCESS;
}
static int
t_insert2_find(void)
{
init_hash_(1);
int i;
struct test_node *node;
for (i = 0; i < MAX_NUM; i++)
{
nodes[i].key = i;
node = &nodes[i];
HASH_INSERT2(hash, TEST, my_pool, node);
}
bt_assert_msg(hash.order != 1, "The hash should auto-resize from order 2^1. The order of the hash is 2^%u.", hash.order);
validate_filled_hash();
return BT_SUCCESS;
}
static int
t_walk(void)
{
init_hash();
fill_hash();
uint i;
uint check[MAX_NUM];
for (i = 0; i < MAX_NUM; i++)
check[i] = 0;
HASH_WALK(hash, next, n)
{
check[n->key]++;
}
HASH_WALK_END;
for (i = 0; i < MAX_NUM; i++)
bt_assert(check[i] == 1);
return BT_SUCCESS;
}
static int
t_walk_delsafe_delete(void)
{
init_hash();
fill_hash();
HASH_WALK_DELSAFE(hash, next, n)
{
HASH_DELETE(hash, TEST, n->key);
}
HASH_WALK_DELSAFE_END;
validate_empty_hash();
return BT_SUCCESS;
}
static int
t_walk_delsafe_remove(void)
{
init_hash();
fill_hash();
HASH_WALK_DELSAFE(hash, next, n)
{
HASH_REMOVE(hash, TEST, n);
}
HASH_WALK_DELSAFE_END;
validate_empty_hash();
return BT_SUCCESS;
}
static int
t_walk_delsafe_delete2(void)
{
init_hash();
fill_hash();
HASH_WALK_DELSAFE(hash, next, n)
{
HASH_DELETE2(hash, TEST, my_pool, n->key);
}
HASH_WALK_DELSAFE_END;
validate_empty_hash();
return BT_SUCCESS;
}
static int
t_walk_delsafe_remove2(void)
{
init_hash();
fill_hash();
HASH_WALK_DELSAFE(hash, next, n)
{
HASH_REMOVE2(hash, TEST, my_pool, n);
}
HASH_WALK_DELSAFE_END;
validate_empty_hash();
return BT_SUCCESS;
}
static int
t_walk_filter(void)
{
init_hash();
fill_hash();
uint i;
uint check[MAX_NUM];
for (i = 0; i < MAX_NUM; i++)
check[i] = 0;
HASH_WALK_FILTER(hash, next, n, m)
{
bt_assert(n == *m);
check[n->key]++;
}
HASH_WALK_FILTER_END;
for (i = 0; i < MAX_NUM; i++)
bt_assert(check[i] == 1);
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_insert_find, "HASH_INSERT and HASH_FIND");
bt_test_suite(t_insert_find_random, "HASH_INSERT pseudo-random keys and HASH_FIND");
bt_test_suite(t_insert2_find, "HASH_INSERT2 and HASH_FIND. HASH_INSERT2 is HASH_INSERT and a smart auto-resize function");
bt_test_suite(t_walk, "HASH_WALK");
bt_test_suite(t_walk_delsafe_delete, "HASH_WALK_DELSAFE and HASH_DELETE");
bt_test_suite(t_walk_delsafe_delete2, "HASH_WALK_DELSAFE and HASH_DELETE2. HASH_DELETE2 is HASH_DELETE and smart auto-resize function");
bt_test_suite(t_walk_delsafe_remove, "HASH_WALK_DELSAFE and HASH_REMOVE");
bt_test_suite(t_walk_delsafe_remove2, "HASH_WALK_DELSAFE and HASH_REMOVE2. HASH_REMOVE2 is HASH_REMOVE and smart auto-resize function");
bt_test_suite(t_walk_filter, "HASH_WALK_FILTER");
return bt_exit_value();
}

186
lib/heap_test.c Normal file
View file

@ -0,0 +1,186 @@
/*
* BIRD Library -- Universal Heap Macros Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "sysdep/config.h"
#include "lib/heap.h"
#define MAX_NUM 1000
#define SPECIAL_KEY -3213
#define MY_CMP(x, y) ((x) < (y))
#define MY_HEAP_SWAP(heap,a,b,t) \
do { \
bt_debug("swap(%u %u) ", a, b); \
HEAP_SWAP(heap,a,b,t); \
} while(0)
static int heap[MAX_NUM+1];
static uint num;
/*
* A valid heap must follow these rules:
* - `num >= 0`
* - `heap[i] >= heap[i / 2]` for each `i` in `[2, num]`
*/
static int
is_heap_valid(int heap[], uint num)
{
uint i;
if (num > MAX_NUM)
return 0;
for (i = 2; i <= num; i++)
if (heap[i] < heap[i / 2])
return 0;
return 1;
}
static void
show_heap(void)
{
uint i;
bt_debug("\n");
bt_debug("numbers %u; ", num);
for (i = 0; i <= num; i++)
bt_debug("%d ", heap[i]);
bt_debug(is_heap_valid(heap, num) ? "OK" : "NON-VALID HEAP!");
bt_debug("\n");
}
static void
init_heap(void)
{
uint i;
num = 0;
heap[0] = SPECIAL_KEY; /* heap[0] should be unused */
for (i = 1; i <= MAX_NUM; i++)
heap[i] = 0;
}
static int
t_heap_insert(void)
{
uint i;
init_heap();
for (i = MAX_NUM; i >= 1; i--)
{
bt_debug("ins %u at pos %u ", i, MAX_NUM - i);
heap[MAX_NUM - i + 1] = i;
HEAP_INSERT(heap, ++num, int, MY_CMP, MY_HEAP_SWAP);
show_heap();
bt_assert(is_heap_valid(heap, num));
}
return BT_SUCCESS;
}
static int
t_heap_increase_decrease(void)
{
uint i;
t_heap_insert();
for (i = 1; i <= MAX_NUM; i++)
{
if ((int)i > heap[i])
{
bt_debug("inc %u ", i);
heap[i] = i;
HEAP_INCREASE(heap, num, int, MY_CMP, MY_HEAP_SWAP, i);
}
else if ((int)i < heap[i])
{
bt_debug("dec %u ", i);
heap[i] = i;
HEAP_INCREASE(heap, num, int, MY_CMP, MY_HEAP_SWAP, i);
}
show_heap();
bt_assert(is_heap_valid(heap, num));
}
return BT_SUCCESS;
}
static int
t_heap_delete(void)
{
uint i;
t_heap_insert();
for (i = 1; i <= num; i++)
{
bt_debug("del at pos %u ", i);
HEAP_DELETE(heap, num, int, MY_CMP, MY_HEAP_SWAP, i);
show_heap();
bt_assert(is_heap_valid(heap, num));
}
return BT_SUCCESS;
}
static int
t_heap_0(void)
{
init_heap();
t_heap_insert();
t_heap_increase_decrease();
t_heap_delete();
return (heap[0] == SPECIAL_KEY) ? BT_SUCCESS : BT_FAILURE;
}
static int
t_heap_insert_random(void)
{
int i, j;
int expected[MAX_NUM+1];
init_heap();
for (i = 1; i <= MAX_NUM; i++)
{
heap[i] = expected[i] = bt_random();
HEAP_INSERT(heap, ++num, int, MY_CMP, MY_HEAP_SWAP);
show_heap();
bt_assert(is_heap_valid(heap, num));
}
for (i = 1; i <= MAX_NUM; i++)
for (j = 1; j <= MAX_NUM; j++)
if(expected[i] == heap[j])
break;
else if (j == MAX_NUM)
{
show_heap();
bt_abort_msg("Did not find a number %d in heap.", expected[i]);
}
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_heap_insert, "Inserting a descending sequence of numbers (the worst case)");
bt_test_suite(t_heap_insert_random, "Inserting pseudo-random numbers");
bt_test_suite(t_heap_increase_decrease, "Increasing/Decreasing");
bt_test_suite(t_heap_delete, "Deleting");
bt_test_suite(t_heap_0, "Is a heap[0] really unused?");
return bt_exit_value();
}

View file

@ -306,7 +306,7 @@ ip6_pton(const char *a, ip6_addr *o)
if (*a == ':' && a[1])
a++;
else if (*a == '.' && (i == 6 || i < 6 && hfil >= 0))
else if (*a == '.' && (i == 6 || (i < 6 && hfil >= 0)))
{ /* Embedded IPv4 address */
ip4_addr x;
if (!ip4_pton(start, &x))

161
lib/ip_test.c Normal file
View file

@ -0,0 +1,161 @@
/*
* BIRD Library -- IP address functions Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/ip.h"
#define IP4_MAX_LEN 16
static int
test_ipa_pton(void *out_, const void *in_, const void *expected_out_)
{
ip_addr *out = out_;
const char *in = in_;
const ip_addr *expected_out = expected_out_;
if (ipa_is_ip4(*expected_out))
{
ip4_addr ip4;
bt_assert(ip4_pton(in, &ip4));
*out = ipa_from_ip4(ip4);
}
else
{
bt_assert(ip6_pton(in, out));
/* ip_addr == ip6_addr */
}
return ipa_equal(*out, *expected_out) ? BT_SUCCESS : BT_FAILURE;
}
static int
t_ip4_pton(void)
{
struct bt_pair test_vectors[] = {
{
.in = "192.168.1.128",
.out = & ipa_build4(192, 168, 1, 128),
},
{
.in = "255.255.255.255",
.out = & ipa_build4(255, 255, 255, 255),
},
{
.in = "0.0.0.0",
.out = & ipa_build4(0, 0, 0, 0),
},
};
return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
}
static int
t_ip6_pton(void)
{
struct bt_pair test_vectors[] = {
{
.in = "2001:0db8:0000:0000:0000:0000:1428:57ab",
.out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
},
{
.in = "2001:0db8:0000:0000:0000::1428:57ab",
.out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
},
{
.in = "2001:0db8::1428:57ab",
.out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
},
{
.in = "2001:db8::1428:57ab",
.out = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
},
{
.in = "::1",
.out = & ipa_build6(0x00000000, 0x00000000, 0x00000000, 0x00000001),
},
{
.in = "::",
.out = & ipa_build6(0x00000000, 0x00000000, 0x00000000, 0x00000000),
},
{
.in = "2605:2700:0:3::4713:93e3",
.out = & ipa_build6(0x26052700, 0x00000003, 0x00000000, 0x471393E3),
},
};
return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
}
static int
test_ipa_ntop(void *out_, const void *in_, const void *expected_out_)
{
char *out = out_;
const ip_addr *in = in_;
const char *expected_out = expected_out_;
if (ipa_is_ip4(*in))
ip4_ntop(ipa_to_ip4(*in), out);
else
ip6_ntop(ipa_to_ip6(*in), out);
int result = strncmp(out, expected_out, ipa_is_ip4(*in) ? IP4_MAX_TEXT_LENGTH : IP6_MAX_TEXT_LENGTH) == 0;
return result ? BT_SUCCESS : BT_FAILURE;
}
static int
t_ip4_ntop(void)
{
struct bt_pair test_vectors[] = {
{
.in = & ipa_build4(192, 168, 1, 128),
.out = "192.168.1.128",
},
{
.in = & ipa_build4(255, 255, 255, 255),
.out = "255.255.255.255",
},
{
.in = & ipa_build4(0, 0, 0, 1),
.out = "0.0.0.1",
},
};
return bt_assert_batch(test_vectors, test_ipa_ntop, bt_fmt_ipa, bt_fmt_str);
}
static int
t_ip6_ntop(void)
{
struct bt_pair test_vectors[] = {
{
.in = & ipa_build6(0x20010DB8, 0x00000000, 0x00000000, 0x142857AB),
.out = "2001:db8::1428:57ab",
},
{
.in = & ipa_build6(0x26052700, 0x00000003, 0x00000000, 0x471393E3),
.out = "2605:2700:0:3::4713:93e3",
},
};
return bt_assert_batch(test_vectors, test_ipa_ntop, bt_fmt_ipa, bt_fmt_str);
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_ip4_pton, "Converting IPv4 string to ip4_addr struct");
bt_test_suite(t_ip6_pton, "Converting IPv6 string to ip6_addr struct");
bt_test_suite(t_ip4_ntop, "Converting ip4_addr struct to IPv4 string");
bt_test_suite(t_ip6_ntop, "Converting ip6_addr struct to IPv6 string");
return bt_exit_value();
}

287
lib/lists_test.c Normal file
View file

@ -0,0 +1,287 @@
/*
* BIRD Library -- Linked Lists Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/lists.h"
#define MAX_NUM 1000
static node nodes[MAX_NUM];
static list l;
static void
show_list(void)
{
bt_debug("\n");
bt_debug("list.null is at %p and point to %p\n", &l.null, l.null);
bt_debug("list.head is at %p and point to %p\n", &l.head, l.head);
bt_debug("list.tail is at %p and point to %p\n", &l.tail, l.tail);
int i;
for (i = 0; i < MAX_NUM; i++)
{
bt_debug("n[%3i] is at %p\n", i, &nodes[i]);
bt_debug(" prev is at %p and point to %p\n", &(nodes[i].prev), nodes[i].prev);
bt_debug(" next is at %p and point to %p\n", &(nodes[i].next), nodes[i].next);
}
}
static int
is_filled_list_well_linked(void)
{
int i;
bt_assert(l.head == &nodes[0]);
bt_assert(l.tail == &nodes[MAX_NUM-1]);
bt_assert((void *) nodes[0].prev == (void *) &l.head);
bt_assert((void *) nodes[MAX_NUM-1].next == (void *) &l.null);
for (i = 0; i < MAX_NUM; i++)
{
if (i < (MAX_NUM-1))
bt_assert(nodes[i].next == &nodes[i+1]);
if (i > 0)
bt_assert(nodes[i].prev == &nodes[i-1]);
}
return 1;
}
static int
is_empty_list_well_unlinked(void)
{
int i;
bt_assert(l.head == NODE &l.null);
bt_assert(l.tail == NODE &l.head);
bt_assert(EMPTY_LIST(l));
for (i = 0; i < MAX_NUM; i++)
{
bt_assert(nodes[i].next == NULL);
bt_assert(nodes[i].prev == NULL);
}
return 1;
}
static void
init_list__(list *l, struct node nodes[])
{
init_list(l);
int i;
for (i = 0; i < MAX_NUM; i++)
{
nodes[i].next = NULL;
nodes[i].prev = NULL;
}
}
static void
init_list_(void)
{
init_list__(&l, (node *) nodes);
}
static int
t_add_tail(void)
{
int i;
init_list_();
for (i = 0; i < MAX_NUM; i++)
{
add_tail(&l, &nodes[i]);
bt_debug(".");
bt_assert(l.tail == &nodes[i]);
bt_assert(l.head == &nodes[0]);
bt_assert((void *) nodes[i].next == (void *) &l.null);
if (i > 0)
{
bt_assert(nodes[i-1].next == &nodes[i]);
bt_assert(nodes[i].prev == &nodes[i-1]);
}
}
show_list();
bt_assert(is_filled_list_well_linked());
return BT_SUCCESS;
}
static int
t_add_head(void)
{
int i;
init_list_();
for (i = MAX_NUM-1; i >= 0; i--)
{
add_head(&l, &nodes[i]);
bt_debug(".");
bt_assert(l.head == &nodes[i]);
bt_assert(l.tail == &nodes[MAX_NUM-1]);
if (i < MAX_NUM-1)
{
bt_assert(nodes[i+1].prev == &nodes[i]);
bt_assert(nodes[i].next == &nodes[i+1]);
}
}
show_list();
bt_assert(is_filled_list_well_linked());
return BT_SUCCESS;
}
static void
insert_node_(node *n, node *after)
{
insert_node(n, after);
bt_debug(".");
}
static int
t_insert_node(void)
{
int i;
init_list_();
// add first node
insert_node_(&nodes[0], NODE &l.head);
// add odd nodes
for (i = 2; i < MAX_NUM; i+=2)
insert_node_(&nodes[i], &nodes[i-2]);
// add even nodes
for (i = 1; i < MAX_NUM; i+=2)
insert_node_(&nodes[i], &nodes[i-1]);
bt_debug("\n");
bt_assert(is_filled_list_well_linked());
return BT_SUCCESS;
}
static void
fill_list2(list *l, node nodes[])
{
int i;
for (i = 0; i < MAX_NUM; i++)
add_tail(l, &nodes[i]);
}
static void
fill_list(void)
{
fill_list2(&l, (node *) nodes);
}
static int
t_remove_node(void)
{
int i;
init_list_();
/* Fill & Remove & Check */
fill_list();
for (i = 0; i < MAX_NUM; i++)
rem_node(&nodes[i]);
bt_assert(is_empty_list_well_unlinked());
/* Fill & Remove the half of nodes & Check & Remove the rest nodes & Check */
fill_list();
for (i = 0; i < MAX_NUM; i+=2)
rem_node(&nodes[i]);
int tail_node_index = (MAX_NUM % 2) ? MAX_NUM - 2 : MAX_NUM - 1;
bt_assert(l.head == &nodes[1]);
bt_assert(l.tail == &nodes[tail_node_index]);
bt_assert(nodes[tail_node_index].next == NODE &l.null);
for (i = 1; i < MAX_NUM; i+=2)
{
if (i > 1)
bt_assert(nodes[i].prev == &nodes[i-2]);
if (i < tail_node_index)
bt_assert(nodes[i].next == &nodes[i+2]);
}
for (i = 1; i < MAX_NUM; i+=2)
rem_node(&nodes[i]);
bt_assert(is_empty_list_well_unlinked());
return BT_SUCCESS;
}
static int
t_replace_node(void)
{
node head, inside, tail;
init_list_();
fill_list();
replace_node(&nodes[0], &head);
bt_assert(l.head == &head);
bt_assert(head.prev == NODE &l.head);
bt_assert(head.next == &nodes[1]);
bt_assert(nodes[1].prev == &head);
replace_node(&nodes[MAX_NUM/2], &inside);
bt_assert(nodes[MAX_NUM/2-1].next == &inside);
bt_assert(nodes[MAX_NUM/2+1].prev == &inside);
bt_assert(inside.prev == &nodes[MAX_NUM/2-1]);
bt_assert(inside.next == &nodes[MAX_NUM/2+1]);
replace_node(&nodes[MAX_NUM-1], &tail);
bt_assert(l.tail == &tail);
bt_assert(tail.prev == &nodes[MAX_NUM-2]);
bt_assert(tail.next == NODE &l.null);
bt_assert(nodes[MAX_NUM-2].next == &tail);
return BT_SUCCESS;
}
static int
t_add_tail_list(void)
{
node nodes2[MAX_NUM];
list l2;
init_list__(&l, (node *) nodes);
fill_list2(&l, (node *) nodes);
init_list__(&l2, (node *) nodes2);
fill_list2(&l2, (node *) nodes2);
add_tail_list(&l, &l2);
bt_assert(nodes[MAX_NUM-1].next == &nodes2[0]);
bt_assert(nodes2[0].prev == &nodes[MAX_NUM-1]);
bt_assert(l.tail == &nodes2[MAX_NUM-1]);
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_add_tail, "Adding nodes to tail of list");
bt_test_suite(t_add_head, "Adding nodes to head of list");
bt_test_suite(t_insert_node, "Inserting nodes to list");
bt_test_suite(t_remove_node, "Removing nodes from list");
bt_test_suite(t_replace_node, "Replacing nodes in list");
bt_test_suite(t_add_tail_list, "At the tail of a list adding the another list");
return bt_exit_value();
}

1159
lib/mac_test.c Normal file

File diff suppressed because it is too large Load diff

149
lib/patmatch_test.c Normal file
View file

@ -0,0 +1,149 @@
/*
* BIRD Library -- Pattern Matching Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "nest/bird.h"
#include "lib/string.h"
#define MATCH (int) { 1 }
#define NOMATCH (int) { 0 }
struct match_pair {
byte *pattern;
byte *data;
};
static int
test_matching(void *out_, const void *in_, const void *expected_out_)
{
int *out = out_;
const struct match_pair *in = in_;
const int *expected_out = expected_out_;
*out = patmatch(in->pattern, in->data);
return (*out == *expected_out) ? BT_SUCCESS : BT_FAILURE;
}
static void
fmt_match_pair(char *buf, size_t size, const void *data)
{
const struct match_pair *mp = data;
snprintf(buf, size, "pattern: '%s', subject: '%s'", mp->pattern, mp->data);
}
static void
fmt_match_result(char *buf, size_t size, const void *data)
{
const int *result = data;
snprintf(buf, size, *result ? "match" : "no-match");
}
static int
t_matching(void)
{
struct bt_pair test_vectors[] = {
{
.in = & (struct match_pair) {
.pattern = "",
.data = "",
},
.out = & MATCH,
},
{
.in = & (struct match_pair) {
.pattern = "*",
.data = "",
},
.out = & MATCH,
},
{
.in = & (struct match_pair) {
.pattern = "\\*",
.data = "*",
},
.out = & MATCH,
},
{
.in = & (struct match_pair) {
.pattern = "\\*",
.data = "a",
},
.out = & NOMATCH,
},
{
.in = & (struct match_pair) {
.pattern = "?",
.data = "",
},
.out = & NOMATCH,
},
{
.in = & (struct match_pair) {
.pattern = "abcdefghijklmnopqrstuvwxyz",
.data = "abcdefghijklmnopqrstuvwxyz",
},
.out = & MATCH,
},
{
.in = & (struct match_pair) {
.pattern = "??????????????????????????",
.data = "abcdefghijklmnopqrstuvwxyz",
},
.out = & MATCH,
},
{
.in = & (struct match_pair) {
.pattern = "*abcdefghijklmnopqrstuvwxyz*",
.data = "abcdefghijklmnopqrstuvwxyz",
},
.out = & MATCH,
},
{
.in = & (struct match_pair) {
.pattern = "ab?defg*jklmnop*stu*wxy*z",
.data = "abcdefghijklmnopqrstuvwxyz",
},
.out = & MATCH,
},
{
.in = & (struct match_pair) {
.pattern = "abcdefghijklmnopqrstuvwxyz",
.data = "abcdefghijklmnopqrtuvwxyz",
},
.out = & NOMATCH,
},
{
.in = & (struct match_pair) {
.pattern = "abcdefghijklmnopqr?uvwxyz",
.data = "abcdefghijklmnopqrstuvwxyz",
},
.out = & NOMATCH,
},
{
.in = & (struct match_pair) {
.pattern = "aa*aaaaa?aaaaaaaaaaaaaaaaaaa",
.data = "aaaaaaaaaaaaaaaaaaaaaaaaaa",
},
.out = & NOMATCH,
},
};
return bt_assert_batch(test_vectors, test_matching, fmt_match_pair, fmt_match_result);
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_matching, "Pattern matching");
return bt_exit_value();
}

70
lib/printf_test.c Normal file
View file

@ -0,0 +1,70 @@
/*
* BIRD Library -- String Functions Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/string.h"
#define BSPRINTF(nw, res, buf, fmt, ...) \
do { \
int n = bsprintf(buf, fmt, ##__VA_ARGS__); \
bt_assert_msg(n == nw, "fmt=\"%s\" returns n=%d, want %d", fmt, n, nw); \
bt_assert_msg(buf[n] == 0, "fmt=\"%s\" buf[%d] should be \'\\0\', found 0x%02x", fmt, n, buf[n]); \
bt_assert_msg(memcmp(buf, res, nw) == 0, "fmt=\"%s\" writes \"%*s\", want \"%*s\"", fmt, (n < nw ? n : nw), buf, nw, res); \
} while (0)
static int
t_simple(void)
{
char buf[256];
memset(buf, 0xa5, 256);
BSPRINTF(0, "", buf, "", NULL);
BSPRINTF(1, "%", buf, "%%", NULL);
BSPRINTF(2, "%%", buf, "%%%%", NULL);
BSPRINTF(1, "\x00", buf, "%c", 0);
BSPRINTF(1, "@", buf, "@", 64);
BSPRINTF(1, "\xff", buf, "%c", 0xff);
errno = 5;
BSPRINTF(18, "Input/output error", buf, "%m");
errno = 0;
BSPRINTF(18, "Input/output error", buf, "%M", 5);
BSPRINTF(11, "TeSt%StRiNg", buf, "%s", "TeSt%StRiNg");
if (sizeof(void *) == 4)
BSPRINTF(8, "1a15600d", buf, "%p", (void *) 0x1a15600d);
else
BSPRINTF(16, "00000fee1a15600d", buf, "%p", (void *) 0xfee1a15600d);
long ln = 0;
BSPRINTF(10, "TeStStRiNg", buf, "TeStS%lntRiNg", &ln);
bt_assert_msg(ln == 5, "fmt=\"TeStS%%lntRiNg\", &ln makes ln=%ld, want 5", ln);
BSPRINTF(2, "%d", buf, "%%d", 1);
BSPRINTF(1, "1", buf, "%d", 1);
BSPRINTF(2, "+1", buf, "%+d", 1);
BSPRINTF(2, " 1", buf, "% d", 1);
BSPRINTF(2, "-1", buf, "%d", -1);
BSPRINTF(11, "-2147483648", buf, "%d", -2147483648);
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_simple, "printf without varargs");
return bt_exit_value();
}

384
lib/slist_test.c Normal file
View file

@ -0,0 +1,384 @@
/*
* BIRD Library -- Safe Linked Lists Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/slists.h"
#define MAX_NUM 1000
static snode nodes[MAX_NUM];
static slist lst;
static void
show_list(void)
{
bt_debug("\n");
bt_debug("list.null is at %p and point to %p \n", &lst.null, lst.null);
bt_debug("list.head is at %p and point to %p \n", &lst.head, lst.head);
bt_debug("list.tail is at %p and point to %p \n", &lst.tail, lst.tail);
bt_debug("list.tail_readers is at %p and point to %p \n", &lst.tail_readers, lst.tail_readers);
int i;
for (i = 0; i < MAX_NUM; i++)
bt_debug("n[%3i] is at %p, .prev (%p) points to %p, .next (%p) points to %p, .readers (%p) points to %p \n",
i, &nodes[i], &(nodes[i].prev), nodes[i].prev, &(nodes[i].next), nodes[i].next, &(nodes[i].readers), nodes[i].readers);
}
static int
is_filled_list_well_linked(void)
{
int i;
bt_assert(lst.head == &nodes[0]);
bt_assert(lst.tail == &nodes[MAX_NUM-1]);
bt_assert((void *) nodes[0].prev == (void *) &lst.head);
bt_assert((void *) nodes[MAX_NUM-1].next == (void *) &lst.null);
for (i = 0; i < MAX_NUM; i++)
{
if (i < (MAX_NUM-1))
bt_assert(nodes[i].next == &nodes[i+1]);
if (i > 0)
bt_assert(nodes[i].prev == &nodes[i-1]);
}
return 1;
}
static int
is_empty_list_well_unlinked(void)
{
bt_assert(lst.head == SNODE &lst.null);
bt_assert(lst.tail == SNODE &lst.head);
bt_assert(EMPTY_SLIST(lst));
return 1;
}
static void
init_list__(slist *l, struct snode nodes[])
{
s_init_list(l);
int i;
for (i = 0; i < MAX_NUM; i++)
{
nodes[i].next = NULL;
nodes[i].prev = NULL;
}
}
static void
init_list_(void)
{
init_list__(&lst, nodes);
}
static int
t_add_tail(void)
{
int i;
init_list_();
for (i = 0; i < MAX_NUM; i++)
{
s_add_tail(&lst, &nodes[i]);
bt_debug(".");
bt_assert(lst.tail == &nodes[i]);
bt_assert(lst.head == &nodes[0]);
bt_assert((void *) nodes[i].next == (void *) &lst.null);
if (i > 0)
{
bt_assert(nodes[i-1].next == &nodes[i]);
bt_assert(nodes[i].prev == &nodes[i-1]);
}
}
bt_assert(is_filled_list_well_linked());
return BT_SUCCESS;
}
static int
t_add_head(void)
{
int i;
init_list_();
for (i = MAX_NUM-1; i >= 0; i--)
{
s_add_head(&lst, &nodes[i]);
bt_debug(".");
bt_assert(lst.head == &nodes[i]);
bt_assert(lst.tail == &nodes[MAX_NUM-1]);
if (i < MAX_NUM-1)
{
bt_assert(nodes[i+1].prev == &nodes[i]);
bt_assert(nodes[i].next == &nodes[i+1]);
}
}
bt_assert(is_filled_list_well_linked());
return BT_SUCCESS;
}
static void
insert_node_(snode *n, snode *after)
{
s_insert_node(n, after);
bt_debug(".");
}
static int
t_insert_node(void)
{
int i;
init_list_();
// add first node
insert_node_(&nodes[0], SNODE &lst.head);
// add odd nodes
for (i = 2; i < MAX_NUM; i+=2)
insert_node_(&nodes[i], &nodes[i-2]);
// add even nodes
for (i = 1; i < MAX_NUM; i+=2)
insert_node_(&nodes[i], &nodes[i-1]);
bt_debug("\n");
bt_assert(is_filled_list_well_linked());
return BT_SUCCESS;
}
static void
fill_list2(slist *l, snode nodes[])
{
int i;
for (i = 0; i < MAX_NUM; i++)
s_add_tail(l, &nodes[i]);
}
static void
fill_list(void)
{
fill_list2(&lst, SNODE nodes);
}
static int
t_remove_node(void)
{
int i;
init_list_();
/* Fill & Remove & Check */
fill_list();
for (i = 0; i < MAX_NUM; i++)
s_rem_node(&nodes[i]);
bt_assert(is_empty_list_well_unlinked());
/* Fill & Remove the half of nodes & Check & Remove the rest nodes & Check */
fill_list();
for (i = 0; i < MAX_NUM; i+=2)
s_rem_node(&nodes[i]);
int tail_node_index = (MAX_NUM % 2) ? MAX_NUM - 2 : MAX_NUM - 1;
bt_assert(lst.head == &nodes[1]);
bt_assert(lst.tail == &nodes[tail_node_index]);
bt_assert(nodes[tail_node_index].next == SNODE &lst.null);
for (i = 1; i < MAX_NUM; i+=2)
{
if (i > 1)
bt_assert(nodes[i].prev == &nodes[i-2]);
if (i < tail_node_index)
bt_assert(nodes[i].next == &nodes[i+2]);
}
for (i = 1; i < MAX_NUM; i+=2)
s_rem_node(&nodes[i]);
bt_assert(is_empty_list_well_unlinked());
return BT_SUCCESS;
}
static int
t_add_tail_list(void)
{
snode nodes2[MAX_NUM];
slist l2;
init_list__(&lst, SNODE &nodes);
fill_list2(&lst, SNODE &nodes);
init_list__(&l2, SNODE &nodes2);
fill_list2(&l2, SNODE &nodes2);
s_add_tail_list(&lst, &l2);
bt_assert(nodes[MAX_NUM-1].next == &nodes2[0]);
bt_assert(nodes2[0].prev == &nodes[MAX_NUM-1]);
bt_assert(lst.tail == &nodes2[MAX_NUM-1]);
return BT_SUCCESS;
}
void
dump(const char *str, slist *a)
{
snode *x;
bt_debug("%s \n", str);
for (x = SHEAD(*a); x; x = x->next)
{
siterator *i, *j;
bt_debug("%p", x);
j = (siterator *) x;
for (i = x->readers; i; i = i->next)
{
if (i->prev != j)
bt_debug(" ???");
j = i;
bt_debug(" [%p:%p]", i, i->node);
}
bt_debug("\n");
}
bt_debug("---\n");
}
static int
t_iterator_walk(void)
{
snode *node;
siterator iter;
init_list_();
fill_list();
int k;
int i = 0;
show_list();
s_init(&iter, &lst);
WALK_SLIST(node, lst)
{
s_get(&iter);
s_put(&iter, node);
bt_debug("node->readers: %p, iter: %p, nodes[%d].readers: %p, node: %p, nodes[i]: %p, node->next: %p \n",
node->readers, &iter, i, nodes[i].readers, node, &(nodes[i]), node->next);
bt_assert(node->readers == &iter);
bt_assert(node->readers == nodes[i].readers);
bt_assert(node == &(nodes[i]));
for (k = 0; k < MAX_NUM; k++)
if (k != i)
bt_assert(nodes[k].readers == NULL);
dump("",&lst);
i++;
}
return BT_SUCCESS;
}
static int
t_original(void)
{
slist a, b;
snode *x, *y;
siterator i, j;
s_init_list(&a);
s_init_list(&b);
x = xmalloc(sizeof(*x));
s_add_tail(&a, x);
x = xmalloc(sizeof(*x));
s_add_tail(&a, x);
x = xmalloc(sizeof(*x));
s_add_tail(&a, x);
dump("1", &a);
s_init(&i, &a);
s_init(&j, &a);
dump("2", &a);
x = s_get(&i);
bt_debug("Got %p\n", x);
dump("3", &a);
s_put(&i, x->next);
dump("4", &a);
y = s_get(&j);
while (y)
{
s_put(&j, y);
dump("5*", &a);
y = s_get(&j)->next;
}
dump("5 done", &a);
s_rem_node(a.head->next);
dump("6 (deletion)", &a);
s_put(&i, s_get(&i)->next);
dump("6 (relink)", &a);
x = xmalloc(sizeof(*x));
s_add_tail(&b, x);
dump("7 (second list)", &b);
s_add_tail_list(&b, &a);
dump("8 (after merge)", &b);
return BT_SUCCESS;
}
static int
t_safe_del_walk(void)
{
init_list_();
fill_list();
show_list();
snode *node, *node_next;
WALK_SLIST_DELSAFE(node,node_next, lst)
{
bt_debug("Will remove node %p \n", node);
s_rem_node(SNODE node);
}
bt_assert(is_empty_list_well_unlinked());
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_add_tail, "Adding nodes to tail of list");
bt_test_suite(t_add_head, "Adding nodes to head of list");
bt_test_suite(t_insert_node, "Inserting nodes to list");
bt_test_suite(t_remove_node, "Removing nodes from list");
bt_test_suite(t_add_tail_list, "At the tail of a list adding the another list");
bt_test_suite(t_iterator_walk, "Iterator walk");
bt_test_suite(t_safe_del_walk, "WALK_SLIST_DELSAFE and s_rem_node all nodes");
bt_test_suite(t_original, "The original BIRD test suit for SLIST");
return bt_exit_value();
}

View file

@ -150,85 +150,3 @@ s_add_tail_list(slist *to, slist *l)
to->tail = q;
s_merge((snode *) &l->null, (snode *) &to->null);
}
#ifdef TEST
#include "lib/resource.h"
#include <stdio.h>
void dump(char *c, slist *a)
{
snode *x;
puts(c);
for(x=SHEAD(*a); x; x=x->next)
{
siterator *i, *j;
printf("%p", x);
j = (siterator *) x;
for(i=x->readers; i; i=i->next)
{
if (i->prev != j)
printf(" ???");
j = i;
printf(" [%p:%p]", i, i->node);
}
putchar('\n');
}
puts("---");
}
int main(void)
{
slist a, b;
snode *x, *y;
siterator i, j;
s_init_list(&a);
s_init_list(&b);
x = xmalloc(sizeof(*x));
s_add_tail(&a, x);
x = xmalloc(sizeof(*x));
s_add_tail(&a, x);
x = xmalloc(sizeof(*x));
s_add_tail(&a, x);
dump("1", &a);
s_init(&i, &a);
s_init(&j, &a);
dump("2", &a);
x = s_get(&i);
printf("Got %p\n", x);
dump("3", &a);
s_put(&i, x->next);
dump("4", &a);
y = s_get(&j);
while (y)
{
s_put(&j, y);
dump("5*", &a);
y = s_get(&j)->next;
}
dump("5 done", &a);
s_rem_node(a.head->next);
dump("6 (deletion)", &a);
s_put(&i, s_get(&i)->next);
dump("6 (relink)", &a);
x = xmalloc(sizeof(*x));
s_add_tail(&b, x);
dump("7 (second list)", &b);
s_add_tail_list(&b, &a);
dump("8 (after merge)", &b);
return 0;
}
#endif

View file

@ -2,3 +2,7 @@ src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_src := a-set_test.c a-path_test.c
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -20,7 +20,7 @@
#define put_as put_u32
#define get_as get_u32
#define BS 4
#define BS 4 /* Base (default) size of ASN (autonomous system number) */
struct adata *
as_path_prepend(struct linpool *pool, struct adata *olda, u32 as)
@ -499,7 +499,6 @@ pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
* (auxiliary position after last real position in AS path)
* is marked.
*/
int
as_path_match(struct adata *path, struct f_path_mask *mask)
{

214
nest/a-path_test.c Normal file
View file

@ -0,0 +1,214 @@
/*
* BIRD -- Path Operations Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "test/bt-utils.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "lib/resource.h"
#define TESTS_NUM 30
#define AS_PATH_LENGTH 1000
#if AS_PATH_LENGTH > AS_PATH_MAXLEN
#warning "AS_PATH_LENGTH should be <= AS_PATH_MAXLEN"
#endif
static int
t_as_path_match(void)
{
resource_init();
int round;
for (round = 0; round < TESTS_NUM; round++)
{
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
u32 first_prepended, last_prepended;
first_prepended = last_prepended = 0;
struct linpool *lp = lp_new(&root_pool, 0);
struct f_path_mask mask[AS_PATH_LENGTH] = {};
int i;
for (i = 0; i < AS_PATH_LENGTH; i++)
{
u32 val = bt_random();
as_path = as_path_prepend(lp, as_path, val);
bt_debug("Prepending ASN: %10u \n", val);
if (i == 0)
first_prepended = val;
if (i == AS_PATH_LENGTH-1)
last_prepended = val;
mask[i].kind = PM_ASN;
mask[i].val = val;
if (i)
mask[i].next = &mask[i-1];
}
bt_assert_msg(as_path_match(as_path, &mask[AS_PATH_LENGTH-1]), "Mask should match with AS path");
u32 asn;
bt_assert(as_path_get_first(as_path, &asn));
bt_assert_msg(asn == last_prepended, "as_path_get_first() should return the last prepended ASN");
bt_assert(as_path_get_last(as_path, &asn));
bt_assert_msg(asn == first_prepended, "as_path_get_last() should return the first prepended ASN");
rfree(lp);
}
return BT_SUCCESS;
}
static int
t_path_format(void)
{
resource_init();
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new(&root_pool, 0);
uint i;
for (i = 4294967285; i <= 4294967294; i++)
{
as_path = as_path_prepend(lp, as_path, i);
bt_debug("Prepending ASN: %10u \n", i);
}
#define BUFFER_SIZE 26
byte buf[BUFFER_SIZE] = {};
as_path_format(as_path, buf, BUFFER_SIZE);
bt_assert_msg(strcmp(buf, "4294967294 4294967293 ...") == 0, "Buffer(%zu): '%s'", strlen(buf), buf);
#define SMALL_BUFFER_SIZE 25
byte buf2[SMALL_BUFFER_SIZE] = {};
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
bt_assert_msg(strcmp(buf2, "4294967294 ...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
rfree(lp);
return BT_SUCCESS;
}
static int
count_asn_in_array(const u32 *array, u32 asn)
{
int counts_of_contains = 0;
int u;
for (u = 0; u < AS_PATH_LENGTH; u++)
if (array[u] == asn)
counts_of_contains++;
return counts_of_contains;
}
static int
t_path_include(void)
{
resource_init();
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new(&root_pool, 0);
u32 as_nums[AS_PATH_LENGTH] = {};
int i;
for (i = 0; i < AS_PATH_LENGTH; i++)
{
u32 val = bt_random();
as_nums[i] = val;
as_path = as_path_prepend(lp, as_path, val);
}
for (i = 0; i < AS_PATH_LENGTH; i++)
{
int counts_of_contains = count_asn_in_array(as_nums, as_nums[i]);
bt_assert_msg(as_path_contains(as_path, as_nums[i], counts_of_contains), "AS Path should contains %d-times number %d", counts_of_contains, as_nums[i]);
bt_assert(as_path_filter(lp, as_path, NULL, as_nums[i], 0) != NULL);
bt_assert(as_path_filter(lp, as_path, NULL, as_nums[i], 1) != NULL);
}
for (i = 0; i < 10000; i++)
{
u32 test_val = bt_random();
int counts_of_contains = count_asn_in_array(as_nums, test_val);
int result = as_path_contains(as_path, test_val, (counts_of_contains == 0 ? 1 : counts_of_contains));
if (counts_of_contains)
bt_assert_msg(result, "As path should contain %d-times the number %u", counts_of_contains, test_val);
else
bt_assert_msg(result == 0, "As path should not contain the number %u", test_val);
}
rfree(lp);
return BT_SUCCESS;
}
static int
t_as_path_converting(void)
{
resource_init();
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new(&root_pool, 0);
#define AS_PATH_LENGTH_FOR_CONVERTING_TEST 10
int i;
for (i = 0; i < AS_PATH_LENGTH_FOR_CONVERTING_TEST; i++)
as_path = as_path_prepend(lp, as_path, i);
bt_debug("data length: %u \n", as_path->length);
byte buffer[100] = {};
int used_size = as_path_convert_to_new(as_path, buffer, AS_PATH_LENGTH_FOR_CONVERTING_TEST-1);
bt_debug("as_path_convert_to_new: len %d \n%s\n", used_size, buffer);
for (i = 0; i < used_size; i++)
{
bt_debug("\\03%d", buffer[i]);
}
bt_debug("\n");
bt_assert(memcmp(buffer,
"\032\039\030\030\030\030\030\030\030\039\030\030\030\030\030\030\030\038\030\030\030\030\030\030"
"\030\037\030\030\030\030\030\030\030\036\030\030\030\030",
38));
bzero(buffer, sizeof(buffer));
int new_used;
used_size = as_path_convert_to_old(as_path, buffer, &new_used);
bt_debug("as_path_convert_to_old: len %d, new_used: %d \n", used_size, new_used);
for (i = 0; i < used_size; i++)
{
bt_debug("\\03%d", buffer[i]);
}
bt_debug("\n");
bt_assert(memcmp(buffer,
"\032\0310\030\039\030\038\030\037\030\036\030\035\030\034\030\033\030\032\030\031\030\030",
22));
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_as_path_match, "Testing AS path matching and some a-path utilities.");
bt_test_suite(t_path_format, "Testing formating as path into byte buffer");
bt_test_suite(t_path_include, "Testing including a AS number in AS path");
bt_test_suite(t_as_path_converting, "Testing as_path_convert_to_*() output constancy");
return bt_exit_value();
}

260
nest/a-set_test.c Normal file
View file

@ -0,0 +1,260 @@
/*
* BIRD -- Set/Community-list Operations Tests
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "test/bt-utils.h"
#include "lib/net.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "lib/resource.h"
#define SET_SIZE 10
static struct adata *set_sequence; /* <0; SET_SIZE) */
static struct adata *set_sequence_same; /* <0; SET_SIZE) */
static struct adata *set_sequence_higher; /* <SET_SIZE; 2*SET_SIZE) */
static struct adata *set_random;
#define BUFFER_SIZE 1000
static byte buf[BUFFER_SIZE] = {};
#define SET_SIZE_FOR_FORMAT_OUTPUT 10
struct linpool *lp;
enum set_type
{
SET_TYPE_INT,
SET_TYPE_EC
};
static void
generate_set_sequence(enum set_type type)
{
struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new(&root_pool, 0);
int i;
for (i = 0; i < SET_SIZE; i++)
{
if (type == SET_TYPE_INT)
{
set_sequence = int_set_add(lp, set_sequence, i);
set_sequence_same = int_set_add(lp, set_sequence_same, i);
set_sequence_higher = int_set_add(lp, set_sequence_higher, i + SET_SIZE);
set_random = int_set_add(lp, set_random, bt_random());
}
else if (type == SET_TYPE_EC)
{
set_sequence = ec_set_add(lp, set_sequence, i);
set_sequence_same = ec_set_add(lp, set_sequence_same, i);
set_sequence_higher = ec_set_add(lp, set_sequence_higher, i + SET_SIZE);
set_random = ec_set_add(lp, set_random, (bt_random() << 32 | bt_random()));
}
else
bt_abort_msg("This should be unreachable");
}
}
/*
* SET INT TESTS
*/
static int
t_set_int_contains(void)
{
int i;
resource_init();
generate_set_sequence(SET_TYPE_INT);
bt_assert(int_set_get_size(set_sequence) == SET_SIZE);
for (i = 0; i < SET_SIZE; i++)
bt_assert(int_set_contains(set_sequence, i));
bt_assert(int_set_contains(set_sequence, -1) == 0);
bt_assert(int_set_contains(set_sequence, SET_SIZE) == 0);
int *data = int_set_get_data(set_sequence);
for (i = 0; i < SET_SIZE; i++)
bt_assert_msg(data[i] == i, "(data[i] = %d) == i = %d)", data[i], i);
rfree(lp);
return BT_SUCCESS;
}
static int
t_set_int_union(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT);
struct adata *set_union;
set_union = int_set_union(lp, set_sequence, set_sequence_same);
bt_assert(int_set_get_size(set_union) == SET_SIZE);
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
set_union = int_set_union(lp, set_sequence, set_sequence_higher);
bt_assert_msg(int_set_get_size(set_union) == SET_SIZE*2, "int_set_get_size(set_union) %d, SET_SIZE*2 %d", int_set_get_size(set_union), SET_SIZE*2);
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
rfree(lp);
return BT_SUCCESS;
}
static int
t_set_int_format(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT);
set_sequence->length = 4 * SET_SIZE_FOR_FORMAT_OUTPUT; /* dirty */
bt_assert(int_set_format(set_sequence, 0, 0, buf, BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "0.0.0.0 0.0.0.1 0.0.0.2 0.0.0.3 0.0.0.4 0.0.0.5 0.0.0.6 0.0.0.7 0.0.0.8 0.0.0.9") == 0);
bzero(buf, BUFFER_SIZE);
bt_assert(int_set_format(set_sequence, 0, 2, buf, BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "0.0.0.2 0.0.0.3 0.0.0.4 0.0.0.5 0.0.0.6 0.0.0.7 0.0.0.8 0.0.0.9") == 0);
bzero(buf, BUFFER_SIZE);
bt_assert(int_set_format(set_sequence, 1, 0, buf, BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9)") == 0);
rfree(lp);
return BT_SUCCESS;
}
static int
t_set_int_delete(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT);
struct adata *deleting_sequence = set_sequence;
u32 i;
for (i = 0; i < SET_SIZE; i++)
{
deleting_sequence = int_set_del(lp, deleting_sequence, i);
bt_assert_msg(int_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i),
"int_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
int_set_get_size(deleting_sequence),
SET_SIZE-1-i);
}
bt_assert(int_set_get_size(set_sequence) == SET_SIZE);
return BT_SUCCESS;
}
/*
* SET EC TESTS
*/
static int
t_set_ec_contains(void)
{
u32 i;
resource_init();
generate_set_sequence(SET_TYPE_EC);
bt_assert(ec_set_get_size(set_sequence) == SET_SIZE);
for (i = 0; i < SET_SIZE; i++)
bt_assert(ec_set_contains(set_sequence, i));
bt_assert(ec_set_contains(set_sequence, -1) == 0);
bt_assert(ec_set_contains(set_sequence, SET_SIZE) == 0);
// int *data = ec_set_get_data(set_sequence);
// for (i = 0; i < SET_SIZE; i++)
// bt_assert_msg(data[i] == (SET_SIZE-1-i), "(data[i] = %d) == ((SET_SIZE-1-i) = %d)", data[i], SET_SIZE-1-i);
rfree(lp);
return BT_SUCCESS;
}
static int
t_set_ec_union(void)
{
resource_init();
generate_set_sequence(SET_TYPE_EC);
struct adata *set_union;
set_union = ec_set_union(lp, set_sequence, set_sequence_same);
bt_assert(ec_set_get_size(set_union) == SET_SIZE);
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
set_union = ec_set_union(lp, set_sequence, set_sequence_higher);
bt_assert_msg(ec_set_get_size(set_union) == SET_SIZE*2, "ec_set_get_size(set_union) %d, SET_SIZE*2 %d", ec_set_get_size(set_union), SET_SIZE*2);
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
rfree(lp);
return BT_SUCCESS;
}
static int
t_set_ec_format(void)
{
resource_init();
struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new(&root_pool, 0);
u64 i = 0;
set_sequence = ec_set_add(lp, set_sequence, i);
for (i = 1; i < SET_SIZE_FOR_FORMAT_OUTPUT; i++)
set_sequence = ec_set_add(lp, set_sequence, i + ((i%2) ? ((u64)EC_RO << 48) : ((u64)EC_RT << 48)));
bt_assert(ec_set_format(set_sequence, 0, buf, BUFFER_SIZE) == 0);
bt_assert_msg(strcmp(buf, "(unknown 0x0, 0, 0) (ro, 0, 1) (rt, 0, 2) (ro, 0, 3) (rt, 0, 4) (ro, 0, 5) (rt, 0, 6) (ro, 0, 7) (rt, 0, 8) (ro, 0, 9)") == 0,
"ec_set_format() returns '%s'", buf);
rfree(lp);
return BT_SUCCESS;
}
static int
t_set_ec_delete(void)
{
resource_init();
generate_set_sequence(SET_TYPE_EC);
struct adata *deleting_sequence = set_sequence;
u32 i;
for (i = 0; i < SET_SIZE; i++)
{
deleting_sequence = ec_set_del(lp, deleting_sequence, i);
bt_assert_msg(ec_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i),
"ec_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
ec_set_get_size(deleting_sequence), SET_SIZE-1-i);
}
bt_assert(ec_set_get_size(set_sequence) == SET_SIZE);
return BT_SUCCESS;
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_set_int_contains, "Testing sets of integers: contains, get_data");
bt_test_suite(t_set_int_format, "Testing sets of integers: format");
bt_test_suite(t_set_int_union, "Testing sets of integers: union");
bt_test_suite(t_set_int_delete, "Testing sets of integers: delete");
bt_test_suite(t_set_ec_contains, "Testing sets of Extended Community values: contains, get_data");
bt_test_suite(t_set_ec_format, "Testing sets of Extended Community values: format");
bt_test_suite(t_set_ec_union, "Testing sets of Extended Community values: union");
bt_test_suite(t_set_ec_delete, "Testing sets of Extended Community values: delete");
return bt_exit_value();
}

View file

@ -10,6 +10,7 @@
#define _BIRD_IFACE_H_
#include "lib/lists.h"
#include "lib/ip.h"
extern list iface_list;

View file

@ -12,7 +12,7 @@
#include "lib/lists.h"
#include "lib/resource.h"
#include "sysdep/unix/timer.h"
//#include "nest/protocol.h"
#include "lib/net.h"
struct ea_list;
struct protocol;

View file

@ -2,3 +2,5 @@ src := bfd.c io.c packets.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -2,3 +2,5 @@ src := attrs.c bgp.c packets.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -2,3 +2,5 @@ src := dbdes.c hello.c iface.c lsack.c lsalib.c lsreq.c lsupd.c neighbor.c ospf.
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -1921,7 +1921,7 @@ rt_sync(struct ospf_proto *p)
/* This is used for forced reload of routes */
int reload = (p->calcrt == 2);
OSPF_TRACE(D_EVENTS, "Starting routing table synchronisation");
OSPF_TRACE(D_EVENTS, "Starting routing table synchronization");
DBG("Now syncing my rt table with nest's\n");
FIB_ITERATE_INIT(&fit, fib);

View file

@ -2,3 +2,5 @@ src := pipe.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -2,3 +2,5 @@ src := packets.c radv.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -311,7 +311,7 @@ radv_check_active(struct proto_radv *ra)
if (!radv_trigger_valid(cf))
return 1;
struct channel *c =ra->p.main_channel;
struct channel *c = ra->p.main_channel;
return rt_examine(c->table, &cf->trigger, &ra->p, c->out_filter);
}

View file

@ -2,3 +2,5 @@ src := packets.c rip.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -2,3 +2,5 @@ src := static.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -67,4 +67,7 @@
/* We have stdint.h */
#undef HAVE_STDINT_H
/* We have execinfo.h */
#undef HAVE_EXECINFO_H
#define CONFIG_PATH ?

View file

@ -3,3 +3,4 @@ obj := $(src-o-files)
$(all-daemon)
$(conf-y-targets): $(s)krt-sock.Y
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -2,3 +2,5 @@ src := netlink.c
obj := $(src-o-files)
$(all-daemon)
$(conf-y-targets): $(s)netlink.Y
tests_objs := $(tests_objs) $(src-o-files)

View file

@ -3,3 +3,6 @@ obj := $(src-o-files)
$(all-daemon)
$(cf-local)
$(conf-y-targets): $(s)krt.Y
src := $(filter-out main.c, $(src))
tests_objs := $(tests_objs) $(src-o-files)

3
test/Makefile Normal file
View file

@ -0,0 +1,3 @@
src := birdtest.c bt-utils.c
obj := $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

488
test/birdtest.c Normal file
View file

@ -0,0 +1,488 @@
/*
* BIRD -- Unit Test Framework (BIRD Test)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include "test/birdtest.h"
#include "lib/string.h"
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#define BACKTRACE_MAX_LINES 100
#define sprintf_concat(s, format, ...) \
snprintf(s + strlen(s), sizeof(s) - strlen(s), format, ##__VA_ARGS__)
static const char *request;
static int list_tests;
static int do_core;
static int no_fork;
static int no_timeout;
static int is_terminal; /* Whether stdout is a live terminal or pipe redirect */
uint bt_verbose;
const char *bt_filename;
const char *bt_test_id;
int bt_result; /* Overall program run result */
int bt_suite_result; /* One suit result */
char bt_out_fmt_buf[1024]; /* Temporary memory buffer for output of testing function */
long int
bt_random(void)
{
/* Seeded in bt_init() */
long int rand_low, rand_high;
rand_low = random();
rand_high = random();
return (rand_low & 0xffff) | ((rand_high & 0xffff) << 16);
}
void
bt_init(int argc, char *argv[])
{
int c;
srandom(BT_RANDOM_SEED);
bt_verbose = 0;
bt_filename = argv[0];
bt_result = BT_SUCCESS;
bt_test_id = NULL;
is_terminal = isatty(fileno(stdout));
while ((c = getopt(argc, argv, "lcftv")) >= 0)
switch (c)
{
case 'l':
list_tests = 1;
break;
case 'c':
do_core = 1;
break;
case 'f':
no_fork = 1;
break;
case 't':
no_timeout = 1;
break;
case 'v':
bt_verbose++;
break;
default:
goto usage;
}
/* Optional requested test_id */
if ((optind + 1) == argc)
request = argv[optind++];
if (optind != argc)
goto usage;
if (do_core)
{
struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY};
int rv = setrlimit(RLIMIT_CORE, &rl);
bt_syscall(rv < 0, "setrlimit RLIMIT_CORE");
}
return;
usage:
printf("Usage: %s [-l] [-c] [-f] [-t] [-vvv] [<test_suit_name>]\n", argv[0]);
printf("Options: \n");
printf(" -l List all test suite names and descriptions \n");
printf(" -c Force unlimit core dumps (needs root privileges) \n");
printf(" -f No forking \n");
printf(" -t No timeout limit \n");
printf(" -v More verbosity, maximum is 3 -vvv \n");
exit(3);
}
static void
bt_dump_backtrace(void)
{
#ifdef HAVE_EXECINFO_H
void *buf[BACKTRACE_MAX_LINES];
char **pp_backtrace;
int lines, j;
if (!bt_verbose)
return;
lines = backtrace(buf, BACKTRACE_MAX_LINES);
bt_log("backtrace() returned %d addresses", lines);
pp_backtrace = backtrace_symbols(buf, lines);
if (pp_backtrace == NULL)
{
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (j = 0; j < lines; j++)
bt_log("%s", pp_backtrace[j]);
free(pp_backtrace);
#endif /* HAVE_EXECINFO_H */
}
static
int bt_run_test_fn(int (*fn)(const void *), const void *fn_arg, int timeout)
{
int result;
alarm(timeout);
if (fn_arg)
result = fn(fn_arg);
else
result = ((int (*)(void))fn)();
if (bt_suite_result != BT_SUCCESS)
result = BT_FAILURE;
return result;
}
static uint
get_num_terminal_cols(void)
{
struct winsize w = {};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
uint cols = w.ws_col;
return (cols > 0 ? cols : 80);
}
/**
* bt_log_result - pretty print of test result
* @result: BT_SUCCESS or BT_FAILURE
* @fmt: a description message (could be long, over more lines)
* @argptr: variable argument list
*
* This function is used for pretty printing of test results on all verbose
* levels.
*/
static void
bt_log_result(int result, const char *fmt, va_list argptr)
{
char fmt_buf[BT_BUFFER_SIZE];
char msg_buf[BT_BUFFER_SIZE];
char *pos;
snprintf(msg_buf, sizeof(msg_buf), "%s%s%s%s",
bt_filename,
bt_test_id ? ": " : "",
bt_test_id ? bt_test_id : "",
(fmt && strlen(fmt) > 0) ? ": " : "");
pos = msg_buf + strlen(msg_buf);
vsnprintf(pos, sizeof(msg_buf) - (pos - msg_buf), fmt, argptr);
/* 'll' means here Last Line */
uint cols = get_num_terminal_cols();
uint ll_len = (strlen(msg_buf) % cols) + BT_PROMPT_OK_FAIL_STRLEN;
uint ll_offset = (ll_len / get_num_terminal_cols() + 1) * cols - BT_PROMPT_OK_FAIL_STRLEN;
uint offset = ll_offset + (strlen(msg_buf) / cols) * cols;
snprintf(fmt_buf, sizeof(fmt_buf), "%%-%us%%s\n", offset);
const char *result_str = is_terminal ? BT_PROMPT_OK : BT_PROMPT_OK_NO_COLOR;
if (result != BT_SUCCESS)
result_str = is_terminal ? BT_PROMPT_FAIL : BT_PROMPT_FAIL_NO_COLOR;
printf(fmt_buf, msg_buf, result_str);
}
/**
* bt_log_overall_result - pretty print of suite case result
* @result: BT_SUCCESS or BT_FAILURE
* @fmt: a description message (could be long, over more lines)
* ...: variable argument list
*
* This function is used for pretty printing of test suite case result.
*/
static void
bt_log_overall_result(int result, const char *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
bt_log_result(result, fmt, argptr);
va_end(argptr);
}
/**
* bt_log_suite_result - pretty print of suite case result
* @result: BT_SUCCESS or BT_FAILURE
* @fmt: a description message (could be long, over more lines)
* ...: variable argument list
*
* This function is used for pretty printing of test suite case result.
*/
void
bt_log_suite_result(int result, const char *fmt, ...)
{
if(bt_verbose >= BT_VERBOSE_SUITE || result == BT_FAILURE)
{
va_list argptr;
va_start(argptr, fmt);
bt_log_result(result, fmt, argptr);
va_end(argptr);
}
}
/**
* bt_log_suite_case_result - pretty print of suite result
* @result: BT_SUCCESS or BT_FAILURE
* @fmt: a description message (could be long, over more lines)
* ...: variable argument list
*
* This function is used for pretty printing of test suite result.
*/
void
bt_log_suite_case_result(int result, const char *fmt, ...)
{
if(bt_verbose >= BT_VERBOSE_SUITE_CASE)
{
va_list argptr;
va_start(argptr, fmt);
bt_log_result(result, fmt, argptr);
va_end(argptr);
}
}
int
bt_test_suite_base(int (*fn)(const void *), const char *id, const void *fn_arg, int forked, int timeout, const char *dsc, ...)
{
if (list_tests)
{
printf("%28s - ", id);
va_list args;
va_start(args, dsc);
vprintf(dsc, args);
va_end(args);
printf("\n");
return BT_SUCCESS;
}
if (no_fork)
forked = 0;
if (no_timeout)
timeout = 0;
if (request && strcmp(id, request))
return BT_SUCCESS;
bt_suite_result = BT_SUCCESS;
bt_test_id = id;
if (bt_verbose >= BT_VERBOSE_ABSOLUTELY_ALL)
bt_log("Starting");
if (!forked)
{
bt_suite_result = bt_run_test_fn(fn, fn_arg, timeout);
}
else
{
pid_t pid = fork();
bt_syscall(pid < 0, "fork");
if (pid == 0)
{
/* child of fork */
_exit(bt_run_test_fn(fn, fn_arg, timeout));
}
int s;
int rv = waitpid(pid, &s, 0);
bt_syscall(rv < 0, "waitpid");
if (WIFEXITED(s))
{
/* Normal exit */
bt_suite_result = WEXITSTATUS(s);
}
else if (WIFSIGNALED(s))
{
/* Stopped by signal */
bt_suite_result = BT_FAILURE;
int sn = WTERMSIG(s);
if (sn == SIGALRM)
{
bt_log("Timeout expired");
}
else if (sn == SIGSEGV)
{
bt_log("Segmentation fault");
bt_dump_backtrace();
}
else if (sn != SIGABRT)
bt_log("Signal %d received", sn);
}
if (WCOREDUMP(s) && bt_verbose)
bt_log("Core dumped");
}
if (bt_suite_result == BT_FAILURE)
bt_result = BT_FAILURE;
bt_log_suite_result(bt_suite_result, NULL);
bt_test_id = NULL;
return bt_suite_result;
}
int
bt_exit_value(void)
{
if (!list_tests || (list_tests && bt_result != BT_SUCCESS))
bt_log_overall_result(bt_result, "");
return bt_result == BT_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;
}
/**
* bt_assert_batch__ - test a batch of inputs/outputs tests
* @opts: includes all necessary data
*
* Should be called using macro bt_assert_batch().
* Returns BT_SUCCESS or BT_FAILURE.
*/
int
bt_assert_batch__(struct bt_batch *opts)
{
int i;
for (i = 0; i < opts->ndata; i++)
{
int bt_suit_case_result = opts->test_fn(opts->out_buf, opts->data[i].in, opts->data[i].out);
if (bt_suit_case_result == BT_FAILURE)
bt_suite_result = BT_FAILURE;
char b[BT_BUFFER_SIZE];
snprintf(b, sizeof(b), "%s(", opts->test_fn_name);
opts->in_fmt(b+strlen(b), sizeof(b)-strlen(b), opts->data[i].in);
sprintf_concat(b, ") gives ");
opts->out_fmt(b+strlen(b), sizeof(b)-strlen(b), opts->out_buf);
if (bt_suit_case_result == BT_FAILURE)
{
sprintf_concat(b, ", but expecting is ");
opts->out_fmt(b+strlen(b), sizeof(b)-strlen(b), opts->data[i].out);
}
bt_log_suite_case_result(bt_suit_case_result, "%s", b);
}
return bt_suite_result;
}
/**
* bt_fmt_str - formating string into output buffer
* @buf: buffer for write
* @size: empty size in @buf
* @data: null-byte terminated string
*
* This function can be used with bt_assert_batch() function.
* Input @data should be const char * string.
*/
void
bt_fmt_str(char *buf, size_t size, const void *data)
{
const byte *s = data;
snprintf(buf, size, "\"");
while (*s)
{
snprintf(buf+strlen(buf), size-strlen(buf), bt_is_char(*s) ? "%c" : "\\%03u", *s);
s++;
}
snprintf(buf+strlen(buf), size-strlen(buf), "\"");
}
/**
* bt_fmt_unsigned - formating unsigned int into output buffer
* @buf: buffer for write
* @size: empty size in @buf
* @data: unsigned number
*
* This function can be used with bt_assert_batch() function.
*/
void
bt_fmt_unsigned(char *buf, size_t size, const void *data)
{
const uint *n = data;
snprintf(buf, size, "0x%x (%u)", *n, *n);
}
/**
* bt_fmt_ipa - formating ip_addr into output buffer
* @buf: buffer for write
* @size: empty size in @buf
* @data: should be struct ip_addr *
*
* This function can be used with bt_assert_batch() function.
*/
void
bt_fmt_ipa(char *buf, size_t size, const void *data)
{
const ip_addr *ip = data;
bsnprintf(buf, size, "%I", *ip);
}
int
bt_is_char(byte c)
{
return (c >= (byte) 32 && c <= (byte) 126);
}
/*
* Mock-ups of all necessary public functions in main.c
*/
char *bird_name;
void async_config(void) {}
void async_dump(void) {}
void async_shutdown(void) {}
void cmd_check_config(char *name UNUSED) {}
void cmd_reconfig(char *name UNUSED, int type UNUSED, int timeout UNUSED) {}
void cmd_reconfig_confirm(void) {}
void cmd_reconfig_undo(void) {}
void cmd_shutdown(void) {}
void cmd_reconfig_undo_notify(void) {}
#include "nest/bird.h"
#include "lib/net.h"
#include "conf/conf.h"
void sysdep_preconfig(struct config *c UNUSED) {}
int sysdep_commit(struct config *new UNUSED, struct config *old UNUSED) { return 0; }
void sysdep_shutdown_done(void) {}
#include "nest/cli.h"
int cli_get_command(cli *c UNUSED) { return 0; }
void cli_write_trigger(cli *c UNUSED) {}
cli *cmd_reconfig_stored_cli;

183
test/birdtest.h Normal file
View file

@ -0,0 +1,183 @@
/*
* BIRD -- Unit Test Framework (BIRD Test)
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRDTEST_H_
#define _BIRDTEST_H_
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include "nest/bird.h"
extern int bt_result;
extern int bt_suite_result;
extern char bt_out_fmt_buf[1024];
extern uint bt_verbose;
#define BT_VERBOSE_NO 0
#define BT_VERBOSE_SUITE 1
#define BT_VERBOSE_SUITE_CASE 2
#define BT_VERBOSE_ABSOLUTELY_ALL 3
extern const char *bt_filename;
extern const char *bt_test_id;
void bt_init(int argc, char *argv[]);
int bt_exit_value(void);
int bt_test_suite_base(int (*test_fn)(const void *), const char *test_id, const void *test_fn_argument, int forked, int timeout, const char *dsc, ...);
long int bt_random(void);
void bt_log_suite_result(int result, const char *fmt, ...);
void bt_log_suite_case_result(int result, const char *fmt, ...);
#define BT_SUCCESS 42 /* 1 is too usual, filter quitbird returns 1 too */
#define BT_FAILURE 0
#define BT_TIMEOUT 5 /* Default timeout in seconds */
#define BT_FORKING 1 /* Forking is enabled in default */
#define BT_RANDOM_SEED 982451653
#define BT_BUFFER_SIZE 10000
#define BT_PROMPT_GREEN "\e[1;32m"
#define BT_PROMPT_RED "\e[1;31m"
#define BT_PROMPT_NORMAL "\e[0m"
#define BT_PROMPT_OK " [" BT_PROMPT_GREEN " OK " BT_PROMPT_NORMAL "] "
#define BT_PROMPT_OK_NO_COLOR " [" " OK " "] "
#define BT_PROMPT_FAIL " [" BT_PROMPT_RED "FAIL" BT_PROMPT_NORMAL "] "
#define BT_PROMPT_FAIL_NO_COLOR " [" "FAIL" "] "
#define BT_PROMPT_OK_FAIL_STRLEN 8 /* strlen ' [FAIL] ' */
#define bt_test_suite(fn, dsc, ...) \
bt_test_suite_extra(fn, BT_FORKING, BT_TIMEOUT, dsc, ##__VA_ARGS__)
#define bt_test_suite_extra(fn, f, t, dsc, ...) \
bt_test_suite_base((int (*)(const void *))fn, #fn, NULL, f, t, dsc, ##__VA_ARGS__)
#define bt_test_suite_arg(fn, arg, dsc, ...) \
bt_test_suite_arg_extra(fn, arg, BT_FORKING, BT_TIMEOUT, dsc, ##__VA_ARGS__)
#define bt_test_suite_arg_extra(fn, arg, f, t, dsc, ...) \
bt_test_suite_base(fn, #fn, arg, f, t, dsc, ##__VA_ARGS__)
#define bt_abort() \
bt_abort_msg("Aborted at %s:%d", __FILE__, __LINE__)
#define bt_abort_msg(format, ...) \
do \
{ \
bt_log(format, ##__VA_ARGS__); \
abort(); \
} while (0)
#define bt_log(format, ...) \
do \
{ \
if (bt_test_id) \
printf("%s: %s: " format "\n", bt_filename, bt_test_id, ##__VA_ARGS__); \
else \
printf("%s: " format "\n", bt_filename, ##__VA_ARGS__); \
} while(0)
#define bt_debug(format, ...) \
do \
{ \
if (bt_verbose >= BT_VERBOSE_ABSOLUTELY_ALL) \
printf(format, ##__VA_ARGS__); \
} while (0)
#define bt_assert(test) \
bt_assert_msg(test, "Assertion (%s) at %s:%d", #test, __FILE__, __LINE__)
#define bt_assert_msg(test, format, ...) \
do \
{ \
int bt_suit_case_result = BT_SUCCESS; \
if ((test) == 0) \
{ \
bt_result = BT_FAILURE; \
bt_suite_result = BT_FAILURE; \
bt_suit_case_result = BT_FAILURE; \
} \
bt_log_suite_case_result(bt_suit_case_result, format, ##__VA_ARGS__); \
} while (0)
#define bt_syscall(test, format, ...) \
do \
{ \
if (test) \
{ \
bt_log(format ": %s", ##__VA_ARGS__, strerror(errno)); \
exit(3); \
} \
} while (0)
#define bt_sprintf_concat(s, format, ...) \
snprintf(s + strlen(s), sizeof(s) - strlen(s), format, ##__VA_ARGS__)
struct bt_pair {
const void *in;
const void *out;
};
/* Data structure used by bt_assert_batch() function */
struct bt_batch {
/* in_fmt / out_fmt - formating data
* @buf: buffer for write stringified @data
* @size: empty size in @buf
* @data: data for stringify
*
* There are some build-in functions, see bt_fmt_* functions */
void (*in_fmt)(char *buf, size_t size, const void *data);
void (*out_fmt)(char *buf, size_t size, const void *data);
/* Temporary output buffer */
void *out_buf;
/* test_fn - testing function
* @out: output data from tested function
* @in: data for input
* @expected_out: expected data from tested function
*
* Input arguments should not be stringified using in_fmt() or out_fmt()
* function already. This function should return only BT_SUCCESS or
* BT_FAILURE */
int (*test_fn)(void *out, const void *in, const void *expected_out);
/* Name of testing function @test_fn */
const char *test_fn_name;
/* Number of items in data*/
int ndata;
/* Array of input and expected output pairs */
struct bt_pair *data;
};
void bt_fmt_str(char *buf, size_t size, const void *data);
void bt_fmt_unsigned(char *buf, size_t size, const void *data);
void bt_fmt_ipa(char *buf, size_t size, const void *data);
int bt_assert_batch__(struct bt_batch *opts);
int bt_is_char(byte c);
#define bt_assert_batch(data__, fn__, in_fmt__, out_fmt__) \
bt_assert_batch__(& (struct bt_batch) { \
.data = data__, \
.ndata = ARRAY_SIZE(data__), \
.test_fn = fn__, \
.test_fn_name = #fn__, \
.in_fmt = in_fmt__, \
.out_fmt = out_fmt__, \
.out_buf = bt_out_fmt_buf, /* Global memory for this usage */ \
})
#endif /* _BIRDTEST_H_ */

223
test/bt-utils.c Normal file
View file

@ -0,0 +1,223 @@
/*
* BIRD Test -- Utils for testing parsing configuration file
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "test/birdtest.h"
#include "test/bt-utils.h"
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "sysdep/unix/unix.h"
#include "sysdep/unix/krt.h"
#include "nest/iface.h"
#include "nest/locks.h"
#include "filter/filter.h"
#define BETWEEN(a, b, c) (((a) >= (b)) && ((a) <= (c)))
static const byte *bt_config_parse_pos;
static uint bt_config_parse_remain_len;
/* This is cf_read_hook for hard-coded text configuration */
static int
cf_static_read(byte *dest, uint max_len, int fd UNUSED)
{
if (max_len > bt_config_parse_remain_len)
max_len = bt_config_parse_remain_len;
memcpy(dest, bt_config_parse_pos, max_len);
bt_config_parse_pos += max_len;
bt_config_parse_remain_len -= max_len;
return max_len;
}
/* This is cf_read_hook for reading configuration files,
* function is copied from main.c, cf_read() */
static int
cf_file_read(byte *dest, uint max_len, int fd)
{
int l = read(fd, dest, max_len);
if (l < 0)
cf_error("Read error");
return l;
}
void
bt_bird_init(void)
{
if(bt_verbose)
log_init_debug("");
log_switch(bt_verbose != 0, NULL, NULL);
resource_init();
olock_init();
io_init();
rt_init();
if_init();
config_init();
protos_build();
proto_build(&proto_unix_kernel);
proto_build(&proto_unix_iface);
}
void bt_bird_cleanup(void)
{
for (int i = 0; i < EAP_MAX; i++)
attr_class_to_protocol[i] = NULL;
config = new_config = NULL;
}
static char *
bt_load_file(const char *filename, int quiet)
{
FILE *f = fopen(filename, "rb");
if (!quiet)
bt_assert_msg(f != NULL, "Open %s", filename);
if (f == NULL)
return NULL;
fseek(f, 0, SEEK_END);
long file_size_ = ftell(f);
fseek(f, 0, SEEK_SET);
if (file_size_ < 0)
return NULL;
size_t file_size = file_size_;
size_t read_size = 0;
char *file_body = mb_allocz(&root_pool, file_size+1);
/* XXX: copied from cf-lex.c */
errno=0;
while ((read_size += fread(file_body+read_size, 1, file_size-read_size, f)) != file_size && ferror(f))
{
bt_debug("iteration \n");
if(errno != EINTR)
{
bt_abort_msg("errno: %d", errno);
break;
}
errno=0;
clearerr(f);
}
fclose(f);
if (!quiet)
bt_assert_msg(read_size == file_size, "Read %s", filename);
return file_body;
}
static void
bt_show_cfg_error(const struct config *cfg)
{
int lino = 0;
int lino_delta = 5;
int lino_err = cfg->err_lino;
const char *str = bt_load_file(cfg->err_file_name, 1);
while (str && *str)
{
lino++;
if (BETWEEN(lino, lino_err - lino_delta, lino_err + lino_delta))
bt_debug("%4u%s", lino, (lino_err == lino ? " >> " : " "));
do
{
if (BETWEEN(lino, lino_err - lino_delta, lino_err + lino_delta))
bt_debug("%c", *str);
} while (*str && *(str++) != '\n');
}
bt_debug("\n");
}
static struct config *
bt_config_parse__(struct config *cfg)
{
bt_assert_msg(config_parse(cfg) == 1, "Parse %s", cfg->file_name);
if (cfg->err_msg)
{
bt_debug("Parse error %s, line %d: %s\n", cfg->err_file_name, cfg->err_lino, cfg->err_msg);
bt_show_cfg_error(cfg);
return NULL;
}
config_commit(cfg, RECONFIG_HARD, 0);
new_config = cfg;
return cfg;
}
struct config *
bt_config_parse(const char *cfg_str)
{
struct config *cfg = config_alloc("configuration");
bt_config_parse_pos = cfg_str;
bt_config_parse_remain_len = strlen(cfg_str);
cf_read_hook = cf_static_read;
return bt_config_parse__(cfg);
}
struct config *
bt_config_file_parse(const char *filepath)
{
struct config *cfg = config_alloc(filepath);
cfg->file_fd = open(filepath, O_RDONLY);
bt_assert_msg(cfg->file_fd > 0, "Open %s", filepath);
if (cfg->file_fd < 0)
return NULL;
cf_read_hook = cf_file_read;
return bt_config_parse__(cfg);
}
/*
* Returns @base raised to the power of @power.
*/
uint
bt_naive_pow(uint base, uint power)
{
uint result = 1;
uint i;
for (i = 0; i < power; i++)
result *= base;
return result;
}
/**
* bytes_to_hex - transform data into hexadecimal representation
* @buf: preallocated string buffer
* @in_data: data for transformation
* @size: the length of @in_data
*
* This function transforms @in_data of length @size into hexadecimal
* representation and writes it into @buf.
*/
void
bt_bytes_to_hex(char *buf, const byte *in_data, size_t size)
{
size_t i;
for(i = 0; i < size; i++)
sprintf(buf + i*2, "%02x", in_data[i]);
}

35
test/bt-utils.h Normal file
View file

@ -0,0 +1,35 @@
/*
* BIRD Test -- Utils for testing parsing configuration file
*
* (c) 2015 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRDTEST_UTILS_H_
#define _BIRDTEST_UTILS_H_
#include "sysdep/config.h"
#define PRIip4 "%d.%d.%d.%d"
#define ARGip4(x) (_I(x) >> 24) & 0xff, (_I(x) >> 16) & 0xff, (_I(x) >> 8) & 0xff, _I(x) & 0xff
#define PRIip6 "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X"
#define ARGip6_HIGH(x,i) (((x).addr[(i)] >> 16) & 0xffff)
#define ARGip6_LOW(x,i) ((x).addr[(i)] & 0xffff)
#define ARGip6_BOTH(x,i) ARGip6_HIGH(x,i), ARGip6_LOW(x,i)
#define ARGip6(x) ARGip6_BOTH((x), 0), ARGip6_BOTH((x), 1), ARGip6_BOTH((x), 2), ARGip6_BOTH((x), 3)
#define BT_CONFIG_PARSE_ROUTER_ID "router id 1.1.1.1; \n"
#define BT_CONFIG_PARSE_STATIC_PROTO "protocol static { ipv4; } \n"
#define BT_CONFIG_SIMPLE BT_CONFIG_PARSE_ROUTER_ID BT_CONFIG_PARSE_STATIC_PROTO
uint bt_naive_pow(uint base, uint power);
void bt_bytes_to_hex(char *buf, const byte *in_data, size_t size);
void bt_bird_init(void);
void bt_bird_cleanup(void);
struct config *bt_config_parse(const char *cfg);
struct config *bt_config_file_parse(const char *filepath);
#endif /* _BIRDTEST_UTILS_H_ */