diff options
author | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2016-11-09 16:36:34 +0100 |
---|---|---|
committer | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2016-11-09 16:36:34 +0100 |
commit | 9b0a0ba9e671d9134b93c33ab73ccccb352acafa (patch) | |
tree | 2a3b007b698c02c72c7bae25d3c7cae6293cd36f /test | |
parent | 8860e991f6650e47cfe6c1af595fe4fe92a4edfd (diff) |
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.
Diffstat (limited to 'test')
-rw-r--r-- | test/Makefile | 3 | ||||
-rw-r--r-- | test/birdtest.c | 488 | ||||
-rw-r--r-- | test/birdtest.h | 183 | ||||
-rw-r--r-- | test/bt-utils.c | 223 | ||||
-rw-r--r-- | test/bt-utils.h | 35 |
5 files changed, 932 insertions, 0 deletions
diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 00000000..2cee9234 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,3 @@ +src := birdtest.c bt-utils.c +obj := $(src-o-files) +tests_objs := $(tests_objs) $(src-o-files) diff --git a/test/birdtest.c b/test/birdtest.c new file mode 100644 index 00000000..4e8645a4 --- /dev/null +++ b/test/birdtest.c @@ -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; diff --git a/test/birdtest.h b/test/birdtest.h new file mode 100644 index 00000000..0090712d --- /dev/null +++ b/test/birdtest.h @@ -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_ */ diff --git a/test/bt-utils.c b/test/bt-utils.c new file mode 100644 index 00000000..aaeb77b8 --- /dev/null +++ b/test/bt-utils.c @@ -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]); +} + diff --git a/test/bt-utils.h b/test/bt-utils.h new file mode 100644 index 00000000..13d267cc --- /dev/null +++ b/test/bt-utils.h @@ -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_ */ |