mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-09-19 11:55:21 +00:00
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:
parent
8860e991f6
commit
9b0a0ba9e6
33
Makefile.in
33
Makefile.in
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -1253,7 +1253,7 @@ foot).
|
||||
<cf/!˜/ 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/˜/
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
87
filter/filter_test.c
Normal 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();
|
||||
}
|
1321
filter/test.conf
1321
filter/test.conf
File diff suppressed because it is too large
Load Diff
@ -2,4 +2,5 @@
|
||||
print "Entering include";
|
||||
print "Should be 2: ", 1+1;
|
||||
print "Leaving include";
|
||||
i = 42;
|
||||
|
||||
|
@ -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,51 +26,58 @@ protocol kernel {
|
||||
protocol static {
|
||||
# disabled;
|
||||
|
||||
import filter { print "ahoj";
|
||||
print source;
|
||||
if source = RTS_STATIC then {
|
||||
print "It is from static";
|
||||
}
|
||||
print from;
|
||||
from = 1.2.3.4;
|
||||
print from;
|
||||
print scope;
|
||||
scope = SCOPE_HOST;
|
||||
print scope;
|
||||
if !(scope ~ [ SCOPE_HOST, SCOPE_SITE ]) then {
|
||||
print "Failed in test";
|
||||
quitbird;
|
||||
}
|
||||
|
||||
preference = 15;
|
||||
print preference;
|
||||
preference = 29;
|
||||
print preference;
|
||||
rip_metric = 1;
|
||||
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";
|
||||
};
|
||||
ipv4 {
|
||||
export all;
|
||||
|
||||
import filter {
|
||||
print "ahoj";
|
||||
print source;
|
||||
if source = RTS_STATIC then {
|
||||
print "It is from static";
|
||||
}
|
||||
print from;
|
||||
from = 1.2.3.4;
|
||||
print from;
|
||||
print scope;
|
||||
scope = SCOPE_HOST;
|
||||
print scope;
|
||||
if !(scope ~ [ SCOPE_HOST, SCOPE_SITE ]) then {
|
||||
print "Failed in test";
|
||||
quitbird;
|
||||
}
|
||||
|
||||
preference = 15;
|
||||
print preference;
|
||||
preference = 29;
|
||||
print preference;
|
||||
rip_metric = 1;
|
||||
print rip_metric;
|
||||
rip_metric = rip_metric + 5;
|
||||
print rip_metric;
|
||||
|
||||
#
|
||||
# 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";
|
||||
}
|
||||
|
@ -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
|
||||
|
113
filter/test_bgp_filtering.conf
Normal file
113
filter/test_bgp_filtering.conf
Normal 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
304
filter/tree_test.c
Normal 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
184
filter/trie_test.c
Normal 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();
|
||||
}
|
@ -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)
|
||||
|
@ -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))
|
||||
@ -150,7 +149,7 @@ void bug(const char *msg, ...) NORET;
|
||||
#define L_FATAL "\010" /* Fatal errors */
|
||||
#define L_BUG "\011" /* BIRD bugs */
|
||||
|
||||
void debug(const char *msg, ...); /* Printf to debug output */
|
||||
void debug(const char *msg, ...); /* Printf to debug output */
|
||||
|
||||
/* Debugging */
|
||||
|
||||
|
@ -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
123
lib/bitops_test.c
Normal 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;
|
||||
}
|
||||