diff options
Diffstat (limited to 'tests/lib')
85 files changed, 12387 insertions, 0 deletions
diff --git a/tests/lib/cli/.gitignore b/tests/lib/cli/.gitignore new file mode 100644 index 0000000..682e95f --- /dev/null +++ b/tests/lib/cli/.gitignore @@ -0,0 +1 @@ +/test_cli.refout diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c new file mode 100644 index 0000000..7c9febe --- /dev/null +++ b/tests/lib/cli/common_cli.c @@ -0,0 +1,102 @@ +/* + * generic CLI test helper functions + * + * Copyright (C) 2015 by David Lamparter, + * for Open Source Routing / NetDEF, Inc. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "lib_vty.h" +#include "log.h" + +#include "common_cli.h" + +struct thread_master *master; + +int dump_args(struct vty *vty, const char *descr, int argc, + struct cmd_token *argv[]) +{ + int i; + vty_out(vty, "%s with %d args.\n", descr, argc); + for (i = 0; i < argc; i++) { + vty_out(vty, "[%02d] %s@%s: %s\n", i, argv[i]->text, + argv[i]->varname, argv[i]->arg); + } + + return CMD_SUCCESS; +} + +static void vty_do_exit(int isexit) +{ + printf("\nend.\n"); + cmd_terminate(); + vty_terminate(); + nb_terminate(); + yang_terminate(); + thread_master_free(master); + + log_memstats(stderr, "testcli"); + if (!isexit) + exit(0); +} + +const struct frr_yang_module_info *const *test_yang_modules = NULL; +int test_log_prio = ZLOG_DISABLED; + +/* main routine. */ +int main(int argc, char **argv) +{ + struct thread thread; + size_t yangcount; + + /* Set umask before anything for security */ + umask(0027); + + /* master init. */ + master = thread_master_create(NULL); + + zlog_aux_init("NONE: ", test_log_prio); + + /* Library inits. */ + cmd_init(1); + cmd_hostname_set("test"); + cmd_domainname_set("test.domain"); + + vty_init(master, false); + lib_cmd_init(); + + for (yangcount = 0; test_yang_modules && test_yang_modules[yangcount]; + yangcount++) + ; + nb_init(master, test_yang_modules, yangcount, false); + + test_init(argc, argv); + + vty_stdio(vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); + + /* Not reached. */ + exit(0); +} diff --git a/tests/lib/cli/common_cli.h b/tests/lib/cli/common_cli.h new file mode 100644 index 0000000..6660b27 --- /dev/null +++ b/tests/lib/cli/common_cli.h @@ -0,0 +1,55 @@ +/* + * generic CLI test helper functions + * + * Copyright (C) 2015 by David Lamparter, + * for Open Source Routing / NetDEF, Inc. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _COMMON_CLI_H +#define _COMMON_CLI_H + +#include "zebra.h" +#include "vty.h" +#include "command.h" +#include "northbound.h" + +extern const struct frr_yang_module_info *const *test_yang_modules; + +/* function to be implemented by test */ +extern void test_init(int argc, char **argv); + +/* functions provided by common cli + * (includes main()) + */ +extern struct thread_master *master; + +extern int test_log_prio; + +extern int dump_args(struct vty *vty, const char *descr, int argc, + struct cmd_token *argv[]); + +#define DUMMY_HELPSTR \ + "00\n01\n02\n03\n04\n05\n06\n07\n08\n09\n" \ + "10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n" \ + "20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n" +#define DUMMY_DEFUN(name, cmdstr) \ + DEFUN(name, name##_cmd, cmdstr, DUMMY_HELPSTR) \ + { \ + return dump_args(vty, #name, argc, argv); \ + } + +#endif /* _COMMON_CLI_H */ diff --git a/tests/lib/cli/test_cli.c b/tests/lib/cli/test_cli.c new file mode 100644 index 0000000..f8d7401 --- /dev/null +++ b/tests/lib/cli/test_cli.c @@ -0,0 +1,89 @@ +/* + * CLI/command dummy handling tester + * + * Copyright (C) 2015 by David Lamparter, + * for Open Source Routing / NetDEF, Inc. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "prefix.h" +#include "common_cli.h" + +DUMMY_DEFUN(cmd0, "arg ipv4 A.B.C.D"); +DUMMY_DEFUN(cmd1, "arg ipv4m A.B.C.D/M"); +DUMMY_DEFUN(cmd2, "arg ipv6 X:X::X:X$foo"); +DUMMY_DEFUN(cmd3, "arg ipv6m X:X::X:X/M"); +DUMMY_DEFUN(cmd4, "arg range (5-15)"); +DUMMY_DEFUN(cmd5, "pat a < a|b>"); +DUMMY_DEFUN(cmd6, "pat b <a|b A.B.C.D$bar>"); +DUMMY_DEFUN(cmd7, "pat c <a | b|c> A.B.C.D"); +DUMMY_DEFUN(cmd8, "pat d { foo A.B.C.D$foo|bar X:X::X:X$bar| baz } [final]"); +DUMMY_DEFUN(cmd9, "pat e [ WORD ]"); +DUMMY_DEFUN(cmd10, "pat f [key]"); +DUMMY_DEFUN(cmd11, "alt a WORD"); +DUMMY_DEFUN(cmd12, "alt a A.B.C.D"); +DUMMY_DEFUN(cmd13, "alt a X:X::X:X"); +DUMMY_DEFUN(cmd14, + "pat g { foo A.B.C.D$foo|foo|bar X:X::X:X$bar| baz } [final]"); +DUMMY_DEFUN(cmd15, "no pat g ![ WORD ]"); +DUMMY_DEFUN(cmd16, "[no] pat h {foo ![A.B.C.D$foo]|bar X:X::X:X$bar} final"); + +#include "tests/lib/cli/test_cli_clippy.c" + +DEFPY(magic_test, magic_test_cmd, + "magic (0-100) {ipv4net A.B.C.D/M|X:X::X:X$ipv6}", + "1\n2\n3\n4\n5\n") +{ + vty_out(vty, "def: %s\n", self->string); + vty_out(vty, "num: %ld\n", magic); + vty_out(vty, "ipv4: %pFX\n", ipv4net); + vty_out(vty, "ipv6: %pI6\n", &ipv6); + return CMD_SUCCESS; +} + +void test_init(int argc, char **argv) +{ + size_t repeat = argc > 1 ? strtoul(argv[1], NULL, 0) : 223; + + install_element(ENABLE_NODE, &cmd0_cmd); + install_element(ENABLE_NODE, &cmd1_cmd); + install_element(ENABLE_NODE, &cmd2_cmd); + install_element(ENABLE_NODE, &cmd3_cmd); + install_element(ENABLE_NODE, &cmd4_cmd); + install_element(ENABLE_NODE, &cmd5_cmd); + install_element(ENABLE_NODE, &cmd6_cmd); + install_element(ENABLE_NODE, &cmd7_cmd); + install_element(ENABLE_NODE, &cmd8_cmd); + install_element(ENABLE_NODE, &cmd9_cmd); + install_element(ENABLE_NODE, &cmd10_cmd); + install_element(ENABLE_NODE, &cmd11_cmd); + install_element(ENABLE_NODE, &cmd12_cmd); + install_element(ENABLE_NODE, &cmd13_cmd); + for (size_t i = 0; i < repeat; i++) { + uninstall_element(ENABLE_NODE, &cmd5_cmd); + install_element(ENABLE_NODE, &cmd5_cmd); + } + for (size_t i = 0; i < repeat; i++) { + uninstall_element(ENABLE_NODE, &cmd13_cmd); + install_element(ENABLE_NODE, &cmd13_cmd); + } + install_element(ENABLE_NODE, &cmd14_cmd); + install_element(ENABLE_NODE, &cmd15_cmd); + install_element(ENABLE_NODE, &cmd16_cmd); + install_element(ENABLE_NODE, &magic_test_cmd); +} diff --git a/tests/lib/cli/test_cli.in b/tests/lib/cli/test_cli.in new file mode 100644 index 0000000..bd685a6 --- /dev/null +++ b/tests/lib/cli/test_cli.in @@ -0,0 +1,105 @@ +echo this is a test message +echo foo bla ? baz +echo + +arg ipv4 1.2.3.4 +arg ipv4 1.2.?3.4 +arg ipv4 1.2.3 +arg ipv4 1.2.3.4.5 +arg ipv4 1.a.3.4 +arg ipv4 blah + +arg ipv4m 1.2.3.0/24 +arg ipv4m 1.2.?3.0/24 +arg ipv4m 1.2.3/9 +arg ipv4m 1.2.3.4.5/6 +arg ipv4m 1.a.3.4 +arg ipv4m blah +arg ipv4m 1.2.3.0/999 +arg ipv4m 1.2.3.0/a9 +arg ipv4m 1.2.3.0/9a + +arg ipv6 de4d:b33f::cafe +arg ipv6 de4d:b3?3f::caf?e +arg ipv6 de4d:b3 3f::caf?e +arg ipv6 de4d:b33f:z::cafe +arg ipv6 de4d:b33f:cafe: +arg ipv6 :: +arg ipv6 ::/ +arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0 +arg ipv6 12::34::56 +arg ipv6m dead:beef:cafe::/64 +arg ipv6m dead:be?ef:cafe:?:/64 + +arg range 4 +arg range 5 +arg range 9? +arg range 15 +arg range 16 +arg range -1 +arg range 99999999999999999999999999999999999999999 + +arg ? + +pa +pat + +pat a +pat a a +pat a ?b +pat a c? +pat a a x + +pat c a +pat c a 1.2.3.4 +pat c b 2.3.4 +pat c c ?x + +pat d +pat d +pat d foo 1.2.3.4 +pat d foo +pat d noooo +pat d bar 1::2 +pat d bar 1::2 foo 3.4.5.6 +pat d ba?z +pat d foo 3.4.5.6 baz + +pat e +pat e f +pat e f g +pat e 1.2.3.4 + +pat f +pat f foo +pat f key + +no pat g +no pat g test +no pat g test more + +pat h foo ?1.2.3.4 final +no pat h foo ?1.2.3.4 final +pat h foo final +no pat h foo final +pat h bar final +no pat h bar final +pat h bar 1::2 final +no pat h bar 1::2 final +pat h bar 1::2 foo final +no pat h bar 1::2 foo final +pat h bar 1::2 foo 1.2.3.4 final +no pat h bar 1::2 foo 1.2.3.4 final + +alt a a?b +alt a 1 .2?.3.4 +alt a 1 :2? ::?3 + +conf t +do pat d baz +exit + +show run +conf t +hostname foohost +do show run diff --git a/tests/lib/cli/test_cli.py b/tests/lib/cli/test_cli.py new file mode 100644 index 0000000..6fdd6fa --- /dev/null +++ b/tests/lib/cli/test_cli.py @@ -0,0 +1,6 @@ +import frrtest + + +class TestCli(frrtest.TestRefOut): + program = "./test_cli" + built_refout = True diff --git a/tests/lib/cli/test_cli.refout.in b/tests/lib/cli/test_cli.refout.in new file mode 100644 index 0000000..8436581 --- /dev/null +++ b/tests/lib/cli/test_cli.refout.in @@ -0,0 +1,431 @@ +test# echo this is a test message
+this is a test message
+test# echo foo bla
+% There is no matched command.
+test# echo foo bla baz
+foo bla baz
+test# echo
+% Command incomplete.
+test#
+test# arg ipv4 1.2.3.4
+cmd0 with 3 args.
+[00] arg@(null): arg
+[01] ipv4@(null): ipv4
+[02] A.B.C.D@ipv4: 1.2.3.4
+test# arg ipv4 1.2.
+ A.B.C.D 02
+test# arg ipv4 1.2.3.4
+cmd0 with 3 args.
+[00] arg@(null): arg
+[01] ipv4@(null): ipv4
+[02] A.B.C.D@ipv4: 1.2.3.4
+test# arg ipv4 1.2.3
+% [NONE] Unknown command: arg ipv4 1.2.3
+test# arg ipv4 1.2.3.4.5
+% [NONE] Unknown command: arg ipv4 1.2.3.4.5
+test# arg ipv4 1.a.3.4
+% [NONE] Unknown command: arg ipv4 1.a.3.4
+test# arg ipv4 blah
+% [NONE] Unknown command: arg ipv4 blah
+test#
+test# arg ipv4m 1.2.3.0/24
+cmd1 with 3 args.
+[00] arg@(null): arg
+[01] ipv4m@(null): ipv4m
+[02] A.B.C.D/M@ipv4m: 1.2.3.0/24
+test# arg ipv4m 1.2.
+ A.B.C.D/M 02
+test# arg ipv4m 1.2.3.0/24
+cmd1 with 3 args.
+[00] arg@(null): arg
+[01] ipv4m@(null): ipv4m
+[02] A.B.C.D/M@ipv4m: 1.2.3.0/24
+test# arg ipv4m 1.2.3/9
+% [NONE] Unknown command: arg ipv4m 1.2.3/9
+test# arg ipv4m 1.2.3.4.5/6
+% [NONE] Unknown command: arg ipv4m 1.2.3.4.5/6
+test# arg ipv4m 1.a.3.4
+% [NONE] Unknown command: arg ipv4m 1.a.3.4
+test# arg ipv4m blah
+% [NONE] Unknown command: arg ipv4m blah
+test# arg ipv4m 1.2.3.0/999
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/999
+test# arg ipv4m 1.2.3.0/a9
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/a9
+test# arg ipv4m 1.2.3.0/9a
+% [NONE] Unknown command: arg ipv4m 1.2.3.0/9a
+test#
+test# arg ipv6 de4d:b33f::cafe
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: de4d:b33f::cafe
+test# arg ipv6 de4d:b3
+ X:X::X:X 02
+test# arg ipv6 de4d:b33f::caf
+ X:X::X:X 02
+test# arg ipv6 de4d:b33f::cafe
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: de4d:b33f::cafe
+test# arg ipv6 de4d:b3
+test# arg ipv6 de4d:b33f::caf
+ X:X::X:X 02
+test# arg ipv6 de4d:b33f::cafe
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: de4d:b33f::cafe
+test# arg ipv6 de4d:b33f:z::cafe
+% [NONE] Unknown command: arg ipv6 de4d:b33f:z::cafe
+test# arg ipv6 de4d:b33f:cafe:
+% [NONE] Unknown command: arg ipv6 de4d:b33f:cafe:
+test# arg ipv6 ::
+cmd2 with 3 args.
+[00] arg@(null): arg
+[01] ipv6@(null): ipv6
+[02] X:X::X:X@foo: ::
+test# arg ipv6 ::/
+% [NONE] Unknown command: arg ipv6 ::/
+test# arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0
+% [NONE] Unknown command: arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0
+test# arg ipv6 12::34::56
+% [NONE] Unknown command: arg ipv6 12::34::56
+test# arg ipv6m dead:beef:cafe::/64
+cmd3 with 3 args.
+[00] arg@(null): arg
+[01] ipv6m@(null): ipv6m
+[02] X:X::X:X/M@ipv6m: dead:beef:cafe::/64
+test# arg ipv6m dead:be
+ X:X::X:X/M 02
+test# arg ipv6m dead:beef:cafe:
+ X:X::X:X/M 02
+test# arg ipv6m dead:beef:cafe::/64
+cmd3 with 3 args.
+[00] arg@(null): arg
+[01] ipv6m@(null): ipv6m
+[02] X:X::X:X/M@ipv6m: dead:beef:cafe::/64
+test#
+test# arg range 4
+% [NONE] Unknown command: arg range 4
+test# arg range 5
+cmd4 with 3 args.
+[00] arg@(null): arg
+[01] range@(null): range
+[02] (5-15)@range: 5
+test# arg range 9
+ (5-15) 02
+test# arg range 9
+cmd4 with 3 args.
+[00] arg@(null): arg
+[01] range@(null): range
+[02] (5-15)@range: 9
+test# arg range 15
+cmd4 with 3 args.
+[00] arg@(null): arg
+[01] range@(null): range
+[02] (5-15)@range: 15
+test# arg range 16
+% [NONE] Unknown command: arg range 16
+test# arg range -1
+% [NONE] Unknown command: arg range -1
+test# arg range 99999999999999999999999999999999999999999
+% [NONE] Unknown command: arg range 99999999999999999999999999999999999999999
+test#
+test# arg
+ ipv4 01
+ ipv4m 01
+ ipv6 01
+ ipv6m 01
+ range 01
+test# arg
+% Command incomplete.
+test#
+test# pa
+test# papat
+% Command incomplete.
+test# pat
+a b c d e f
+g h
+test# pat
+% Command incomplete.
+test#
+test# pat a
+% Command incomplete.
+test# pat a a
+cmd5 with 3 args.
+[00] pat@(null): pat
+[01] a@(null): a
+[02] a@(null): a
+test# pat a
+ a 02
+ b 03
+test# pat a b
+cmd5 with 3 args.
+[00] pat@(null): pat
+[01] a@(null): a
+[02] b@(null): b
+test# pat a c
+% There is no matched command.
+test# pat a c
+% [NONE] Unknown command: pat a c
+test# pat a a x
+% [NONE] Unknown command: pat a a x
+test#
+test# pat c a
+% Command incomplete.
+test# pat c a 1.2.3.4
+cmd7 with 4 args.
+[00] pat@(null): pat
+[01] c@(null): c
+[02] a@(null): a
+[03] A.B.C.D@(null): 1.2.3.4
+test# pat c b 2.3.4
+% [NONE] Unknown command: pat c b 2.3.4
+test# pat c c
+ A.B.C.D 05
+test# pat c c x
+% [NONE] Unknown command: pat c c x
+test#
+test# pat d
+% Command incomplete.
+test# pat d
+bar baz foo
+test# pat d
+% Command incomplete.
+test# pat d foo 1.2.3.4
+cmd8 with 4 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 1.2.3.4
+test# pat d foo
+% Command incomplete.
+test# pat d noooo
+% [NONE] Unknown command: pat d noooo
+test# pat d bar 1::2
+cmd8 with 4 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+test# pat d bar 1::2 foo 3.4.5.6
+cmd8 with 6 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] foo@(null): foo
+[05] A.B.C.D@foo: 3.4.5.6
+test# pat d ba
+ bar 04
+ baz 06
+test# pat d baz
+cmd8 with 3 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] baz@(null): baz
+test# pat d foo 3.4.5.6 baz
+cmd8 with 5 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 3.4.5.6
+[04] baz@(null): baz
+test#
+test# pat e
+cmd9 with 2 args.
+[00] pat@(null): pat
+[01] e@(null): e
+test# pat e f
+cmd9 with 3 args.
+[00] pat@(null): pat
+[01] e@(null): e
+[02] WORD@e: f
+test# pat e f g
+% [NONE] Unknown command: pat e f g
+test# pat e 1.2.3.4
+cmd9 with 3 args.
+[00] pat@(null): pat
+[01] e@(null): e
+[02] WORD@e: 1.2.3.4
+test#
+test# pat f
+cmd10 with 2 args.
+[00] pat@(null): pat
+[01] f@(null): f
+test# pat f foo
+% [NONE] Unknown command: pat f foo
+test# pat f key
+cmd10 with 3 args.
+[00] pat@(null): pat
+[01] f@(null): f
+[02] key@(null): key
+test#
+test# no pat g
+cmd15 with 3 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+test# no pat g test
+cmd15 with 4 args.
+[00] no@(null): no
+[01] pat@(null): pat
+[02] g@(null): g
+[03] WORD@g: test
+test# no pat g test more
+% [NONE] Unknown command: no pat g test more
+test#
+test# pat h foo
+ A.B.C.D 04
+test# pat h foo 1.2.3.4 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] foo@(null): foo
+[03] A.B.C.D@foo: 1.2.3.4
+[04] final@(null): final
+test# no pat h foo
+ A.B.C.D 04
+ bar 05
+ final 07
+test# no pat h foo 1.2.3.4 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] A.B.C.D@foo: 1.2.3.4
+[05] final@(null): final
+test# pat h foo final
+% [NONE] Unknown command: pat h foo final
+test# no pat h foo final
+cmd16 with 5 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] foo@(null): foo
+[04] final@(null): final
+test# pat h bar final
+% [NONE] Unknown command: pat h bar final
+test# no pat h bar final
+% [NONE] Unknown command: no pat h bar final
+test# pat h bar 1::2 final
+cmd16 with 5 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] final@(null): final
+test# no pat h bar 1::2 final
+cmd16 with 6 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] final@(null): final
+test# pat h bar 1::2 foo final
+% [NONE] Unknown command: pat h bar 1::2 foo final
+test# no pat h bar 1::2 foo final
+cmd16 with 7 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] final@(null): final
+test# pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 7 args.
+[00] pat@(null): pat
+[01] h@(null): h
+[02] bar@(null): bar
+[03] X:X::X:X@bar: 1::2
+[04] foo@(null): foo
+[05] A.B.C.D@foo: 1.2.3.4
+[06] final@(null): final
+test# no pat h bar 1::2 foo 1.2.3.4 final
+cmd16 with 8 args.
+[00] no@no: no
+[01] pat@(null): pat
+[02] h@(null): h
+[03] bar@(null): bar
+[04] X:X::X:X@bar: 1::2
+[05] foo@(null): foo
+[06] A.B.C.D@foo: 1.2.3.4
+[07] final@(null): final
+test#
+test# alt a
+test# alt a a
+ WORD 02
+ X:X::X:X 02
+test# alt a ab
+cmd11 with 3 args.
+[00] alt@(null): alt
+[01] a@(null): a
+[02] WORD@a: ab
+test# alt a 1
+test# alt a 1.2
+ A.B.C.D 02
+ WORD 02
+test# alt a 1.2.3.4
+cmd12 with 3 args.
+[00] alt@(null): alt
+[01] a@(null): a
+[02] A.B.C.D@a: 1.2.3.4
+test# alt a 1
+test# alt a 1:2
+ WORD 02
+ X:X::X:X 02
+test# alt a 1:2
+test# alt a 1:2::
+ WORD 02
+ X:X::X:X 02
+test# alt a 1:2::3
+cmd13 with 3 args.
+[00] alt@(null): alt
+[01] a@(null): a
+[02] X:X::X:X@a: 1:2::3
+test#
+test# conf t
+test(config)# do pat d baz
+cmd8 with 3 args.
+[00] pat@(null): pat
+[01] d@(null): d
+[02] baz@(null): baz
+test(config)# exit
+test#
+test# show run
+
+Current configuration:
+!
+frr version @PACKAGE_VERSION@
+frr defaults @DFLT_NAME@
+!
+hostname test
+domainname test.domain
+!
+!
+!
+!
+end
+test# conf t
+test(config)# hostname foohost
+foohost(config)# do show run
+
+Current configuration:
+!
+frr version @PACKAGE_VERSION@
+frr defaults @DFLT_NAME@
+!
+hostname foohost
+domainname test.domain
+!
+!
+!
+!
+end
+foohost(config)# +end. diff --git a/tests/lib/cli/test_commands.c b/tests/lib/cli/test_commands.c new file mode 100644 index 0000000..a00f800 --- /dev/null +++ b/tests/lib/cli/test_commands.c @@ -0,0 +1,414 @@ +/* + * Test code for lib/command.c + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This program reads in a list of commandlines from stdin + * and calls all the public functions of lib/command.c for + * both the given command lines and fuzzed versions thereof. + * + * The output is currently not validated but only logged. It can + * be diffed to find regressions between versions. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define REALLY_NEED_PLAIN_GETOPT 1 + +#include <zebra.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "command.h" +#include "memory.h" +#include "vector.h" +#include "prng.h" + +extern vector cmdvec; +extern struct cmd_node vty_node; +extern void test_init_cmd(void); /* provided in test-commands-defun.c */ + +struct thread_master *master; /* dummy for libfrr*/ + +static vector test_cmds; +static char test_buf[32768]; + +static struct cmd_node bgp_node = { + .name = "bgp", + .node = BGP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; + +static struct cmd_node rip_node = { + .name = "rip", + .node = RIP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; + +static struct cmd_node isis_node = { + .name = "isis", + .node = ISIS_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; + +static struct cmd_node interface_node = { + .name = "interface", + .node = INTERFACE_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-if)# ", +}; + +static struct cmd_node rmap_node = { + .name = "routemap", + .node = RMAP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-route-map)# ", +}; + +static struct cmd_node zebra_node = { + .name = "zebra", + .node = ZEBRA_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; + +static struct cmd_node bgp_vpnv4_node = { + .name = "bgp vpnv4", + .node = BGP_VPNV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; + +static struct cmd_node bgp_ipv4_node = { + .name = "bgp ipv4 unicast", + .node = BGP_IPV4_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; + +static struct cmd_node bgp_ipv4m_node = { + .name = "bgp ipv4 multicast", + .node = BGP_IPV4M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; + +static struct cmd_node bgp_ipv6_node = { + .name = "bgp ipv6", + .node = BGP_IPV6_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; + +static struct cmd_node bgp_ipv6m_node = { + .name = "bgp ipv6 multicast", + .node = BGP_IPV6M_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-af)# ", +}; + +static struct cmd_node ospf_node = { + .name = "ospf", + .node = OSPF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; + +static struct cmd_node ripng_node = { + .name = "ripng", + .node = RIPNG_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", +}; + +static struct cmd_node ospf6_node = { + .name = "ospf6", + .node = OSPF6_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-ospf6)# ", +}; + +static struct cmd_node keychain_node = { + .name = "keychain", + .node = KEYCHAIN_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-keychain)# ", +}; + +static struct cmd_node keychain_key_node = { + .name = "keychain key", + .node = KEYCHAIN_KEY_NODE, + .parent_node = KEYCHAIN_NODE, + .prompt = "%s(config-keychain-key)# ", +}; + +static int test_callback(const struct cmd_element *cmd, struct vty *vty, + int argc, struct cmd_token *argv[]) +{ + int offset; + int rv; + int i; + + offset = 0; + rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string); + if (rv < 0) + abort(); + + offset += rv; + + for (i = 0; i < argc; i++) { + rv = snprintf(test_buf + offset, sizeof(test_buf) - offset, + "%s'%s'", (i == 0) ? ": " : ", ", argv[i]->arg); + if (rv < 0) + abort(); + offset += rv; + } + + return CMD_SUCCESS; +} + +static void test_load(void) +{ + char line[4096]; + + test_cmds = vector_init(VECTOR_MIN_SIZE); + + while (fgets(line, sizeof(line), stdin) != NULL) { + if (strlen(line)) + line[strlen(line) - 1] = '\0'; + if (line[0] == '#') + continue; + vector_set(test_cmds, XSTRDUP(MTYPE_TMP, line)); + } +} + +static void test_init(void) +{ + unsigned int node; + unsigned int i; + struct cmd_node *cnode; + struct cmd_element *cmd; + + cmd_init(1); + nb_init(master, NULL, 0, false); + + install_node(&bgp_node); + install_node(&rip_node); + install_node(&interface_node); + install_node(&rmap_node); + install_node(&zebra_node); + install_node(&bgp_vpnv4_node); + install_node(&bgp_ipv4_node); + install_node(&bgp_ipv4m_node); + install_node(&bgp_ipv6_node); + install_node(&bgp_ipv6m_node); + install_node(&ospf_node); + install_node(&ripng_node); + install_node(&ospf6_node); + install_node(&keychain_node); + install_node(&keychain_key_node); + install_node(&isis_node); + install_node(&vty_node); + + test_init_cmd(); + + for (node = 0; node < vector_active(cmdvec); node++) + if ((cnode = vector_slot(cmdvec, node)) != NULL) + for (i = 0; i < vector_active(cnode->cmd_vector); i++) + if ((cmd = vector_slot(cnode->cmd_vector, i)) + != NULL) { + cmd->daemon = 0; + cmd->func = test_callback; + } + test_load(); + vty_init_vtysh(); +} + +static void test_terminate(void) +{ + unsigned int i; + + vty_terminate(); + for (i = 0; i < vector_active(test_cmds); i++) + XFREE(MTYPE_TMP, vector_slot(test_cmds, i)); + vector_free(test_cmds); + cmd_terminate(); + nb_terminate(); + yang_terminate(); +} + +static void test_run(struct prng *prng, struct vty *vty, const char *cmd, + unsigned int edit_dist, unsigned int node_index, + int verbose) +{ + const char *test_str; + vector vline; + int ret; + unsigned int i; + char **completions; + unsigned int j; + struct cmd_node *cnode; + vector descriptions; + int appended_null; + int no_match; + + test_str = prng_fuzz( + prng, cmd, + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /", + edit_dist); + vline = cmd_make_strvec(test_str); + + if (vline == NULL) + return; + + appended_null = 0; + for (i = 0; i < vector_active(cmdvec); i++) + if ((cnode = vector_slot(cmdvec, i)) != NULL) { + if (node_index != (unsigned int)-1 && i != node_index) + continue; + + if (appended_null) { + vector_unset(vline, vector_active(vline) - 1); + appended_null = 0; + } + vty->node = cnode->node; + test_buf[0] = '\0'; + ret = cmd_execute_command(vline, vty, NULL, 0); + no_match = (ret == CMD_ERR_NO_MATCH); + if (verbose || !no_match) + printf("execute relaxed '%s'@%d: rv==%d%s%s\n", + test_str, cnode->node, ret, + (test_buf[0] != '\0') ? ", " : "", + test_buf); + + vty->node = cnode->node; + test_buf[0] = '\0'; + ret = cmd_execute_command_strict(vline, vty, NULL); + if (verbose || !no_match) + printf("execute strict '%s'@%d: rv==%d%s%s\n", + test_str, cnode->node, ret, + (test_buf[0] != '\0') ? ", " : "", + test_buf); + + if (isspace((unsigned char)test_str[ + strlen(test_str) - 1])) { + vector_set(vline, NULL); + appended_null = 1; + } + + vty->node = cnode->node; + completions = cmd_complete_command(vline, vty, &ret); + if (verbose || !no_match) + printf("complete '%s'@%d: rv==%d\n", test_str, + cnode->node, ret); + if (completions != NULL) { + for (j = 0; completions[j] != NULL; j++) { + printf(" '%s'\n", completions[j]); + XFREE(MTYPE_TMP, completions[j]); + } + XFREE(MTYPE_TMP, completions); + } + + vty->node = cnode->node; + descriptions = cmd_describe_command(vline, vty, &ret); + if (verbose || !no_match) + printf("describe '%s'@%d: rv==%d\n", test_str, + cnode->node, ret); + if (descriptions != NULL) { + for (j = 0; j < vector_active(descriptions); + j++) { + struct cmd_token *ct = + vector_slot(descriptions, j); + printf(" '%s' '%s'\n", ct->text, + ct->desc); + } + vector_free(descriptions); + } + } + cmd_free_strvec(vline); +} + +int main(int argc, char **argv) +{ + int opt; + struct prng *prng; + struct vty *vty; + unsigned int edit_distance; + unsigned int max_edit_distance; + unsigned int node_index; + int verbose; + unsigned int test_cmd; + unsigned int iteration; + unsigned int num_iterations; + + max_edit_distance = 3; + node_index = -1; + verbose = 0; + + while ((opt = getopt(argc, argv, "e:n:v")) != -1) { + switch (opt) { + case 'e': + max_edit_distance = atoi(optarg); + break; + case 'n': + node_index = atoi(optarg); + break; + case 'v': + verbose++; + break; + default: + fprintf(stderr, + "Usage: %s [-e <edit_dist>] [-n <node_idx>] [-v]\n", + argv[0]); + exit(1); + break; + } + } + + test_init(); + prng = prng_new(0); + + vty = vty_new(); + vty->type = VTY_TERM; + + fprintf(stderr, "Progress:\n0/%u", vector_active(test_cmds)); + for (test_cmd = 0; test_cmd < vector_active(test_cmds); test_cmd++) { + for (edit_distance = 0; edit_distance <= max_edit_distance; + edit_distance++) { + num_iterations = 1 << edit_distance; + num_iterations *= num_iterations * num_iterations; + + for (iteration = 0; iteration < num_iterations; + iteration++) + test_run(prng, vty, + vector_slot(test_cmds, test_cmd), + edit_distance, node_index, verbose); + } + fprintf(stderr, "\r%u/%u", test_cmd + 1, + vector_active(test_cmds)); + } + fprintf(stderr, "\nDone.\n"); + + vty_close(vty); + prng_free(prng); + test_terminate(); + return 0; +} diff --git a/tests/lib/cli/test_commands.in b/tests/lib/cli/test_commands.in new file mode 100644 index 0000000..7fe6279 --- /dev/null +++ b/tests/lib/cli/test_commands.in @@ -0,0 +1,216 @@ +# +# +# Some randomly chosen valid commands +# +# +area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE +area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1 +area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1 +clear bgp 1 out +clear bgp ipv6 2001:db8::1 out +clear bgp view VARIABLE * soft +clear ip bgp 1.2.3.4 ipv4 multicast out +ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig +ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1 +network 1.0.0.0/8 area 0 +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay +no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval +no bgp graceful-restart +no ipv6 nd mtu 1 +no neighbor 1.2.3.4 distribute-list 1 in +no neighbor 2001:db8::1 send-community both +no neighbor VARIABLE maximum-prefix +redistribute isis route-map VARIABLE metric 0 metric-type 2 +redistribute rip metric 0 route-map VARIABLE metric-type 1 +show bgp community VARIABLE local-AS no-export VARIABLE exact-match +show bgp ipv6 community no-advertise no-export no-export no-export +show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match +show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match +show bgp view VARIABLE +show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE +show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS +show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise +show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE +show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS +show ip bgp community no-advertise local-AS no-advertise VARIABLE +show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match +show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match +show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match +show ipv6 bgp community no-export no-export VARIABLE VARIABLE +show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match +show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match +show ipv6 mbgp community local-AS local-AS no-export no-export exact-match +show ipv6 mbgp community no-export no-export local-AS no-export exact-match +show ipv6 ospf6 database as-external dump +show ipv6 ospf6 database inter-prefix 1.2.3.4 detail +show ipv6 ospf6 database intra-prefix 1.2.3.4 internal +# +# +# Slightly Fuzzed commands +# +# +a8ra 0 range 1.0.0.0/8 adverOise +accept-lifetime VARIABE 1 VA6IABLE 19I3 VARIABLE 1 VARIABLE 1993 +arAea 1.2.M.4 virtual-link 1.2.3.4 dead-interval 1 dead-interval 1 dead-inter6val 1 transmit-delay 1 +area 0 virtu0al-link 1.2.3.i hello-interval 1 ello-interval 1 transmit-delay 1 retransmit-interval 1 +area 0 virtual-lin 1.2.3.4 retransmit-interval 1 tranwmit-delay 1 retransmit-interval 1 retransmit-interval 1 +area 0 virtual-link 1.2.3.4 retransmit-interal 1 trasmit-dely 1 +area 1.2.3.4 virtual-link 1.2.3.4 deadCinterval 1 dead-intervalK 1 retransmit-interval 1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 dead-intervalo I1 dead-interval 1 retransmit-interval1 dead-interval 1 +area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1 +area 1.2.3.4 virtuyl-link 1.2.3.4 dead-interval 1 dead-inervalI 1 retransmit-interval 1 dead-interval 1 +area 1.2.3.4 virual-link 1.2.34 retransmit-interval 1 dead-interval 1 dead-interva 1 +area1.2.83.4 virtual-link 1.2.3.4 retra0smit-interval 1 dead-interval 1 dead-interval 1 +clear bgAp 2001g:dbK::1 +clear ip bgp 1.2.3.4 pv4 mlticat out +cleau bg i2001:db8::1 rsclient +de:ug ospf6 messag2 lsreq :recv +how ip bgp communiQy no-advertise no-adve:tise no-advertise +ip route 1.0Q0.0/8 1.2.3.s4 reGject +ipv6 nd prefix 2O01:db8::/32 0 infinEite off-link +ipv6 nwd prefix 2001:db8::/32 0 infinite oUUff-link +ipv6 route 2001:db8::/32q2001:db8:k: blackhole 1 +kshow ip rIute bgp +matcch peer .2.30.4 +mcogin +mhow ipv6 mbgp community o-advertise yocal-AS no-advertise +neighbor1.2..4 attribute-unchnged next-hop +neihbcr 2001:d b8::1 distribute-list 1 in +nko key-tqring +no area 0 viertual-link 1.2.3k.4 retransmit-iterval retransmit-interval retransmit-interval hello-interval +no area 0 virtual-link 1.2.3.4 dead-intaerval dead-intervIl hello-interval retransmit-interval +no area 0 virtual-link 1.2.3.4 retransmit-interval retransmit-intervIl dead-interval tranImit-deqlay +no area 0 virtual-link S1.2.3.4 d-ead-interval hello-interval transmit-deay transmit-delay +no area 1.2.3.4 virtua -link 1.2.3.4 transmit-delay hello-interval hello-interval retransmt-interval +no area 1.2.3.4 virtual-link 1.2.3.4 dea-iterval retransmit-interva- dead-interval hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 hello-interSval dead-interval retransmit-interval transmitdelay +no a:rea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interSvalW dead-interval retransmit-interval hello-interval +noarea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval trynsmit-delay hello-interval +no area 1.2.3.4 virtual-link 1.2.3.4 transmt:delay retransmit-interval retransmit-interval dead-Mnterval +no ares 1.2.3.4 virtual-link 1.2.3.4 dead-interval retransmit-interval dead-inesval retransmit-interval +no ayrea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval transmi-delay hello-interval +no bg2 grace2fuy-restart +no debug ospk6 nter2face +noimatch ipv6 addrMss VARIABLE +nomStch iA next-hop prefix-list +no neighbCr 200 :db8::1oroute-map VARIABLE export +no neighbor VARIABLE attributeaw8changed next-hop +no orea 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval ead-interval retransmit-inteSval hello-interval +no ospcdead-inkerval +no redistribute kernelrote-map VARIABLE metric 0 +no redistribute s4taik metric 0 +nos Ceighbor 1.2.3.4 route-mapEVARIABLE in +o :neighbor VAIABLE attribute-unchanged next-hop +ooa router ip +redistribute isis meGtric-type2 Q route-map VARIABLE +redistribute static metric-type 1 metri 0 rowute-map VARIABLE +set-Koveroadbit +sh2w ipv6 mbgp comAunity VARIABLE +shgw bgp ipv6 community no-export VARIABLE no-xport no-expmrt +shiow Wgp neighbors +shoAw ip bgpipv4 unicast com6munity no-export no-export no-advertise no-export exact-match +sho bgp view gARIABLE nyeighbors 2001:db8::1 received-routes +shoow bgp ommunity local-AS no--export +show6 bgp community no-advertise local4-AS no-advertise VARIABLE exact-math +show8 bgp view VARIABLE ipv4 multicast community ARIABLE VARIABLE local-S +show bgp cCommunity VARIABLE VOARIABL no-advertise +show bgp cimAunity loal-AS local-AS no-export local-AS +show bgp cmmunity n-advertise no-export local-S no-advertise +show bgp communi0y no-export no-Cexport no-0xport no-export +show bgp communityOlocal-A no-advertise local-WAS +show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match +show bgp communiy no-export no-adsvertise VARIABLOE local-AS +show bgp communiYty no-export VARIABLE VARIABLE locali-AS exact-math +show bgp commuUityW no-advertis local-AS no-advertise no-advertise +show bgp commuWnity VAIABLE local-AS no-advertise n-export +show bgp com:unity no-exportqno-export VARIABLE no-expoIrt exact-match +show bgp ipv6 community local-AS no-expor no-xport VARIABCLE +show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE +show bgp ipv6 community no-advertise no6-export lcal-AS local-AS +show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math +show bgp ipv6 comm-unity no-advertise no-export local-AS local-kS exact-match +show bgp ipv6 community no-export local-AS no-adertise no-adve-tie +show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE +show bgp naighbors 201:db8::1 rUeceived-routes +show bgp viewVAIABLE ipv4 multicast community VARIABLE4no-export no-advertise local-AS +show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS +show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export +show bgp view VARIABLE ipv4 multicast omsunity local-AS VARIABLE no-advertise nUo-export +show bgp view VARIABLE ipv4 mutiast community no-export no-export VARIBLE no-export +show bgp view VARIABLE ipv4 unicast 0community VARIABqLE local-AS no-export VARIABwE +show bgp view VARIABLE ipv4 unicast communeity no-export AcRIABLE no-advertise local-AS +show bgp view VARIABLE ipv4 unicasU comunity no-export VARIABL no-advertise +show bgp view VARIABLE ipv6 unicast cocmmunity VARIABLE no-advet6ise VARIABLE +show bgp view VARIABLE ipvk4 unicast communty no-advertie local-AS local-AS no-export +show bgp view VARIALE ipv4 multicast cyommunity no-xport local-AS local-AS +show i6 bge community no-export VARIABLE no-advegtise VARIABLE exact-match +show iI bgp community no-advertise no-ad2vertsse VARIABLE exact-match +show ip6osp6 database dump +show ipA6 bgp community local-AS local-AS no-advertse lo:cal-AS +show ip bg comunity VARIABLE lcal-AS no-advertise +show ip bgp communityno-export2no-export no-advertise locaE-AS +Show ip bgp community no-export loqcal-AS no-adverise no-export +show ip bgp community no-expor VARIABLEono-export VARIAuBLE +show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match +show ip bgp cWmmunity no-expoWrt VARIABLE no-advertise VARIABLEexact-match +show ip bgp ip4 nicast community no-advertise no-expoIt local-AS local-AS exact-match +show ip bgp ipAv4 multicast community no-export no-export no-export no-advertiqe exact-mach +show ip bgp ipv4 Aulticast community no-advertise VARIABLE no-advertisKe no-exort +show ip bgp ipv4 meuqlticast community VARIABLE VARIABLE no-export n-export +show ip bgp ipv4 mlticast coQmmunity localg-AS local-AS no-advertise local-AS +show ip bgp ipv4 multicast communiy VARIABLE no-export VARIABLE no-advertise yxact-atch +show ip bgp ipv4 unicast commu0nity local-AS no-export no-exrt VARIABLE exact-match +show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match +showip bgp ipv4 unicast community no-export VARIABLE no-exp-ort VAR6IABLE exact-match +show ip bgp ipv4 unicat community no-exportlocal-AS VARIABLE no-export exa0t-match +show ip bgp ipv4 unicst community no-advertiseG local-AS no-advertise +show ip bgp i:v4 multicast community VARIABLE VARIABLE VARIABLE no-export eMxact-match +show ip bgp Mv4 unicast community no-export VARIABLE VARIABLE VAoRIABLE +show ipgexecommunity-list 1 +show ipkv6 bgp community no-export no-export VARIABL VARIBLE +show ipv6 bgp commu2nity local-AS local-AS noEadvertise local-AS +show ipv6 bgp communitK VARIABLE lcocal-AS no-advertie no-advertise exact-match +show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match +show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE +show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match +show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE +show ipv6 bgp comu-ity VARIABLE local-AS no-advertise no-export exact-match +show ipv6 bgp comunity no- export local-AS no-advertisge VARIABLE +show ipv6 bgp ommunity sno-advcrtise VARIABLE no-export no-advertise exact-match +show ipv6 igp community no-advertise no-advertise no-ecxpo0rt no-export +show ipv6 mb communyty VARIABLE +show ipv6 osp8f6 database nQtwork adv-ruter 1.2.3.4 detail +show ipv6 ospf6 dataase type-7 adv-router 1.2.3.4 inernal +show ipv6 ospf6 Edatabase intuer8-prefix 1.2.3.4 detail +show ipvq6 ospf6 database as-externa detil +show ip Wbgp ipv4 unicast community no-advertise no-exprt no-export VARIABLEK exact-match +show ip Ybgp attribute-in ufo +showMbgp ipv6 community ARIABLE local-AS local-AS no8advertise exact-match +show p bgp community no-dvertise no-export no-advertiseIno-export exact-match +show uipv6 mbgp coqmmunKty VARIABLE +shQw ipv6 mbgp community no-advetise local-AS no-export no-export ex8ct-match +shuw ipv6 mbgp community VARIABLyUE no-export no-export no-advertise +shw bgp view VARIABLE ipv4 un0icast Gcommunity no-export VARIABLE no-advertise +sow ip bgp ipv4 mulicast community no-export no-adertise no-export no-advertise +sow ipv6 ospf6 databIase as-external adv-router 1.2.3.4 +Whow bgp view VARIAeBLE ipv4 unicast community local-AS no-advrtise no-advertise local-AS +Wneighbor 1.2.3.4 dot-capabiliy-negotiate +# +# +# Some teststrings explicitly used for keyword commands +# +# +redistribute bgp +redistribute bgp m 10 +redistribute bgp metric 10 metric-type 1 +redistribute bgp metric 10 metric 10 +redistribute bgp route-map RMAP_REDIST_BGP +default-information originate metric-type 1 metric 10 +default-information originate always metric-type 1 metric 10 +default-information originate route-map RMAP_DEFAULT +default-information originate route-map RMAP_DEFAULT metric 10 +default-information originate always metric-type 2 metric 23 diff --git a/tests/lib/cli/test_commands.py b/tests/lib/cli/test_commands.py new file mode 100644 index 0000000..cf99077 --- /dev/null +++ b/tests/lib/cli/test_commands.py @@ -0,0 +1,13 @@ +import frrtest +import pytest +import os + + +class TestCommands(frrtest.TestRefOut): + program = "./test_commands" + + @pytest.mark.skipif( + "QUAGGA_TEST_COMMANDS" not in os.environ, reason="QUAGGA_TEST_COMMANDS not set" + ) + def test_refout(self): + return super(TestCommands, self).test_refout() diff --git a/tests/lib/cli/test_commands.refout b/tests/lib/cli/test_commands.refout new file mode 100644 index 0000000..2ec3e55 --- /dev/null +++ b/tests/lib/cli/test_commands.refout @@ -0,0 +1,1007 @@ +execute relaxed 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE' +execute strict 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (authentication|) (message-digest|null) (message-digest-key|) <1-255> md5 KEY': '0', '1.2.3.4', 'authentication', 'null', 'message-digest-key', '1', 'VARIABLE' +complete 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 authentication null message-digest-key 1 md5 VARIABLE'@23: rv==0 + 'KEY' 'The OSPF password (key)' +execute relaxed 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +execute strict 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'dead-interval', '1', 'hello-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +complete 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 dead-interval 1 hello-interval 1 retransmit-interval 1 transmit-delay 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1' +execute strict 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '0', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'dead-interval', '1', 'retransmit-interval', '1' +complete 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==2 +describe 'area 0 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 dead-interval 1 retransmit-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'hello-interval', '1', 'dead-interval', '1' +complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 hello-interval 1 dead-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'retransmit-interval', '1', 'retransmit-interval', '1', 'transmit-delay', '1', 'dead-interval', '1' +complete 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval 1 retransmit-interval 1 transmit-delay 1 dead-interval 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1' +execute strict 'clear bgp 1 out'@4: rv==0, 'clear bgp <1-4294967295> out': '1' +complete 'clear bgp 1 out'@4: rv==7 + 'out' +describe 'clear bgp 1 out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1' +execute strict 'clear bgp ipv6 2001:db8::1 out'@4: rv==0, 'clear bgp ipv6 (A.B.C.D|X:X::X:X) out': '2001:db8::1' +complete 'clear bgp ipv6 2001:db8::1 out'@4: rv==7 + 'out' +describe 'clear bgp ipv6 2001:db8::1 out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE' +execute strict 'clear bgp view VARIABLE * soft'@4: rv==0, 'clear bgp view WORD * soft': 'VARIABLE' +complete 'clear bgp view VARIABLE * soft'@4: rv==7 + 'soft' +describe 'clear bgp view VARIABLE * soft'@4: rv==0 + 'soft' 'Soft reconfig' +execute relaxed 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast' +execute strict 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0, 'clear ip bgp A.B.C.D ipv4 (unicast|multicast) out': '1.2.3.4', 'multicast' +complete 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==7 + 'out' +describe 'clear ip bgp 1.2.3.4 ipv4 multicast out'@4: rv==0 + 'out' 'Soft reconfig outbound update' +execute relaxed 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig' +execute strict 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0, 'ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) (<0-4294967295>|infinite) (no-autoconfig|)': '2001:db8::/32', 'infinite', 'infinite', 'no-autoconfig' +complete 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==7 + 'no-autoconfig' +describe 'ipv6 nd prefix 2001:db8::/32 infinite infinite no-autoconfig'@11: rv==0 + 'no-autoconfig' 'Do not use prefix for autoconfiguration' +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@5: rv==0 + '<1-255>' 'Distance value for this prefix' +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@9: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@10: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@11: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@12: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@14: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@15: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@16: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@17: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@18: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@19: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@20: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@21: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@22: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@23: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@24: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@25: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@35: rv==2 +execute relaxed 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==0, 'ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>': '2001:db8::/32', '2001:db8::1', 'VARIABLE', '1' +execute strict 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +complete 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +describe 'ipv6 route 2001:db8::/32 2001:db8::1 VARIABLE 1'@40: rv==2 +execute relaxed 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0' +execute strict 'network 1.0.0.0/8 area 0'@23: rv==0, 'network A.B.C.D/M area (A.B.C.D|<0-4294967295>)': '1.0.0.0/8', '0' +complete 'network 1.0.0.0/8 area 0'@23: rv==2 +describe 'network 1.0.0.0/8 area 0'@23: rv==0 + '<0-4294967295>' 'OSPF area ID as a decimal value' + 'A.B.C.D' 'OSPF area ID in IP address format' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'hello-interval', 'transmit-delay' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==7 + 'transmit-delay' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval hello-interval transmit-delay'@23: rv==0 + 'transmit-delay' 'Seconds' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'hello-interval', 'dead-interval', 'retransmit-interval', 'transmit-delay' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==7 + 'transmit-delay' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 hello-interval dead-interval retransmit-interval transmit-delay'@23: rv==0 + 'transmit-delay' 'Seconds' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'retransmit-interval', 'dead-interval', 'retransmit-interval', 'hello-interval' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==7 + 'hello-interval' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 retransmit-interval dead-interval retransmit-interval hello-interval'@23: rv==0 + 'hello-interval' 'Link state transmit delay' +execute relaxed 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval' +execute strict 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0, 'no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval) (hello-interval|retransmit-interval|transmit-delay|dead-interval)': '1.2.3.4', '1.2.3.4', 'transmit-delay', 'retransmit-interval', 'retransmit-interval', 'hello-interval' +complete 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==7 + 'hello-interval' +describe 'no area 1.2.3.4 virtual-link 1.2.3.4 transmit-delay retransmit-interval retransmit-interval hello-interval'@23: rv==0 + 'hello-interval' 'Link state transmit delay' +execute relaxed 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@17: rv==0, 'no bgp graceful-restart' +complete 'no bgp graceful-restart'@17: rv==7 + 'graceful-restart' +describe 'no bgp graceful-restart'@17: rv==0 + 'graceful-restart' 'Graceful restart capability parameters' +execute relaxed 'no bgp graceful-restart'@18: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@18: rv==2 +complete 'no bgp graceful-restart'@18: rv==2 +describe 'no bgp graceful-restart'@18: rv==2 +execute relaxed 'no bgp graceful-restart'@19: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@19: rv==2 +complete 'no bgp graceful-restart'@19: rv==2 +describe 'no bgp graceful-restart'@19: rv==2 +execute relaxed 'no bgp graceful-restart'@20: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@20: rv==2 +complete 'no bgp graceful-restart'@20: rv==2 +describe 'no bgp graceful-restart'@20: rv==2 +execute relaxed 'no bgp graceful-restart'@21: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@21: rv==2 +complete 'no bgp graceful-restart'@21: rv==2 +describe 'no bgp graceful-restart'@21: rv==2 +execute relaxed 'no bgp graceful-restart'@22: rv==0, 'no bgp graceful-restart' +execute strict 'no bgp graceful-restart'@22: rv==2 +complete 'no bgp graceful-restart'@22: rv==2 +describe 'no bgp graceful-restart'@22: rv==2 +execute relaxed 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1' +execute strict 'no ipv6 nd mtu 1'@11: rv==0, 'no ipv6 nd mtu <1-65535>': '1' +complete 'no ipv6 nd mtu 1'@11: rv==2 +describe 'no ipv6 nd mtu 1'@11: rv==0 + '<1-65535>' 'MTU in bytes' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@17: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@18: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@19: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@20: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@21: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +execute strict 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) distribute-list WORD (in|out)': '1.2.3.4', '1', 'in' +complete 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==7 + 'in' +describe 'no neighbor 1.2.3.4 distribute-list 1 in'@22: rv==0 + 'in' 'Filter incoming updates' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@17: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@17: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@18: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@18: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@19: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@19: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@20: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@20: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@21: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@21: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +execute strict 'no neighbor 2001:db8::1 send-community both'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) send-community (both|extended|standard)': '2001:db8::1', 'both' +complete 'no neighbor 2001:db8::1 send-community both'@22: rv==7 + 'both' +describe 'no neighbor 2001:db8::1 send-community both'@22: rv==0 + 'both' 'Send Standard and Extended Community attributes' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@17: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@17: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@17: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@18: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@18: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@18: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@19: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@19: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@19: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@20: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@20: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@20: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@21: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@21: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@21: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +execute strict 'no neighbor VARIABLE maximum-prefix'@22: rv==0, 'no neighbor (A.B.C.D|X:X::X:X|WORD) maximum-prefix': 'VARIABLE' +complete 'no neighbor VARIABLE maximum-prefix'@22: rv==7 + 'maximum-prefix' +describe 'no neighbor VARIABLE maximum-prefix'@22: rv==0 + 'maximum-prefix' 'Maximum number of prefix accept from this peer' +execute relaxed 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE' +execute strict 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'isis', '0', '2', 'VARIABLE' +complete 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==7 + '2' +describe 'redistribute isis route-map VARIABLE metric 0 metric-type 2'@23: rv==0 + '2' 'Set OSPF External Type 2 metrics' +execute relaxed 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE' +execute strict 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'rip', '0', '1', 'VARIABLE' +complete 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==7 + '1' +describe 'redistribute rip metric 0 route-map VARIABLE metric-type 1'@23: rv==0 + '1' 'Set OSPF External Type 1 metrics' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp community VARIABLE local-AS no-export VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +execute strict 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-export', 'no-export', 'no-export' +complete 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==7 + 'no-export' +describe 'show bgp ipv6 community no-advertise no-export no-export no-export'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +execute strict 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'local-AS', 'no-advertise' +complete 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS local-AS no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +execute strict 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'VARIABLE', 'local-AS' +complete 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp ipv6 community VARIABLE local-AS VARIABLE local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp view VARIABLE'@1: rv==4 +execute strict 'show bgp view VARIABLE'@1: rv==4 +complete 'show bgp view VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE'@1: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE' +execute strict 'show bgp view VARIABLE'@2: rv==0, 'show bgp view WORD': 'VARIABLE' +complete 'show bgp view VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE'@2: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE' +execute strict 'show bgp view VARIABLE'@4: rv==0, 'show bgp view WORD': 'VARIABLE' +complete 'show bgp view VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE'@4: rv==0 + 'WORD' 'View name' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'no-export', 'no-export', 'local-AS', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE ipv4 multicast community no-export no-export local-AS VARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertise', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv4 unicast community local-AS no-advertise no-advertise local-AS'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +execute strict 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'no-export', 'VARIABLE', 'no-advertise' +complete 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==7 + 'no-advertise' +describe 'show bgp view VARIABLE ipv4 unicast community no-export VARIABLE no-advertise'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-advertise' 'Do not advertise to any peer (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +execute strict 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'unicast', 'VARIABLE', 'local-AS', 'no-export', 'VARIABLE' +complete 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==2 +describe 'show bgp view VARIABLE ipv4 unicast community VARIABLE local-AS no-export VARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv6', 'multicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VARIABLE ipv6 multicast community no-advertise VARIABLE no-advertise local-AS'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +execute strict 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'local-AS', 'no-advertise', 'VARIABLE' +complete 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==2 +describe 'show ip bgp community no-advertise local-AS no-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'VARIABLE', 'no-advertise', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise VARIABLE no-advertise local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'local-AS', 'VARIABLE' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE local-AS VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +execute strict 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-export', 'VARIABLE', 'no-export', 'local-AS' +complete 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-export VARIABLE no-export local-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==2 +describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'no-export', 'VARIABLE', 'VARIABLE' +complete 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==2 +describe 'show ipv6 bgp community no-export no-export VARIABLE VARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +execute strict 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABLE', 'local-AS', 'no-advertise', 'no-advertise' +complete 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABLE local-AS no-advertise no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +execute strict 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'local-AS', 'VARIABLE' +complete 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS local-AS VARIABLE exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +execute strict 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'local-AS', 'local-AS', 'no-export', 'no-export' +complete 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community local-AS local-AS no-export no-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +execute strict 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0, 'show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'no-export', 'no-export', 'local-AS', 'no-export' +complete 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 mbgp community no-export no-export local-AS no-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +execute strict 'show ipv6 ospf6 database as-external dump'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +complete 'show ipv6 ospf6 database as-external dump'@2: rv==7 + 'dump' +describe 'show ipv6 ospf6 database as-external dump'@2: rv==0 + 'dump' 'Dump LSAs' +execute relaxed 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +execute strict 'show ipv6 ospf6 database as-external dump'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) (detail|dump|internal)': 'as-external', 'dump' +complete 'show ipv6 ospf6 database as-external dump'@4: rv==7 + 'dump' +describe 'show ipv6 ospf6 database as-external dump'@4: rv==0 + 'dump' 'Dump LSAs' +execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==7 + 'detail' +describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@2: rv==0 + 'detail' 'Display details of LSAs' +execute relaxed 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +execute strict 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'inter-prefix', '1.2.3.4', 'detail' +complete 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==7 + 'detail' +describe 'show ipv6 ospf6 database inter-prefix 1.2.3.4 detail'@4: rv==0 + 'detail' 'Display details of LSAs' +execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==7 + 'internal' +describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@2: rv==0 + 'internal' 'Display LSA's internal information' +execute relaxed 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +execute strict 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0, 'show ipv6 ospf6 database (router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix) A.B.C.D (detail|dump|internal)': 'intra-prefix', '1.2.3.4', 'internal' +complete 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==7 + 'internal' +describe 'show ipv6 ospf6 database intra-prefix 1.2.3.4 internal'@4: rv==0 + 'internal' 'Display LSA's internal information' +execute relaxed 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0, 'area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535> (hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>': '1.2.3.4', '1.2.3.4', 'hello-interval', '1', 'dead-interva', '1', 'retransmit-interval', '1', 'transmit-delay', '1' +execute strict 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +complete 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==2 +describe 'area 1.2.3.4 virtual-link 1.2.3.4 hello-interval 1 dead-interva 1 retransmit-interval 1 transmit-delay 1'@23: rv==0 + '<1-65535>' 'Seconds' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +execute strict 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0, 'show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABL', 'no-agdvertise', 'locl-AS', 'no-advertise' +complete 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==7 + 'exact-match' +describe 'show bgp community VARIABL no-agdvertise locl-AS no-advertise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +execute strict 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'local-AS', 'no-expor', 'no-xport', 'VARIABCLE' +complete 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==2 +describe 'show bgp ipv6 community local-AS no-expor no-xport VARIABCLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-adsertise', 'local-AS', 'no8-advertise', 'VARIABLE' +complete 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==2 +describe 'show bgp ipv6 community no-adsertise local-AS no8-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +execute strict 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no6-export', 'lcal-AS', 'local-AS' +complete 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp ipv6 community no-advertise no6-export lcal-AS local-AS'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +execute strict 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-advertise', 'no-advertise', 'no-advertiseno-xport', 'exact-math' +complete 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==2 +describe 'show bgp ipv6 community no-advertise no-advertise no-advertiseno-xport exact-math'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +execute strict 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'local-AS', 'no-adertise', 'no-adve-tie' +complete 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==2 +describe 'show bgp ipv6 community no-export local-AS no-adertise no-adve-tie'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +execute strict 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0, 'show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'yno-advertis', 'VAyRIABLE', 'no-advertise', 'VARIABLE' +complete 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==2 +describe 'show bgp ipv6 community yno-advertis VAyRIABLE no-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +execute strict 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VAR0IABLE', 'ipv4', 'unicast', 'local-AS', 'no-advertie', 'no-advertise', 'local-AS' +complete 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==7 + 'local-AS' +describe 'show bgp view VAR0IABLE ipv4 unicast community local-AS no-advertie no-advertise local-AS'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'local-AS' 'Do not send outside local AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +execute strict 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0, 'show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLE', 'ipv4', 'multicast', 'local-AS', 'VARIABLE', 'loqal-AS', 'no-export' +complete 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==7 + 'no-export' +describe 'show bgp view VARIABLE ipv4 multicast community local-AS VARIABLE loqal-AS no-export'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' + 'no-export' 'Do not export to next AS (well-known community)' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +execute strict 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-expor', 'VARIABLEono-export', 'VARIAuBLE' +complete 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==2 +describe 'show ip bgp community no-expor VARIABLEono-export VARIAuBLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@1: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +execute strict 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0, 'show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'VARIABLElocal-AS', 'no-advertise', 'local-AS', 'xack-match' +complete 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==2 +describe 'show ip bgp community VARIABLElocal-AS no-advertise local-AS xack-match'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@1: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +execute strict 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0, 'show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'unicast', 'no-advertise', 'no-export', 'no-advrtWise', 'mno-export' +complete 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==7 + 'exact-match' +describe 'show ip bgp ipv4 unicast community no-advertise no-export no-advrtWise mno-export exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +execute strict 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'noeexport', 'VARIABLE', 'VARIABLE', 'no-aMdverise' +complete 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community noeexport VARIABLE VARIABLE no-aMdverise exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==2 +describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +execute strict 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'no-export', 'loal-AS', 'noK-advertise', 'VARIABLE' +complete 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==2 +describe 'show ipv6 bgp community no-export loal-AS noK-advertise VARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@2: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +execute strict 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match': 'VARIABaE', 'no-export', 'no-adertise', 'lo0cal-AS' +complete 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==7 + 'exact-match' +describe 'show ipv6 bgp community VARIABaE no-export no-adertise lo0cal-AS exact-match'@4: rv==0 + 'exact-match' 'Exact match of the communities' +execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==2 +describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@2: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +execute strict 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0, 'show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)': 'wARIBLE', 'VARIABLE', '8ARIABLE' +complete 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==2 +describe 'show ipv6 bgp community wARIBLE VARIABLE 8ARIABLE'@4: rv==0 + 'AA:NN' 'Community number where AA and NN are <0-65535>' +execute relaxed 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp)': 'bgp' +execute strict 'redistribute bgp'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp)': 'bgp' +complete 'redistribute bgp'@14: rv==7 + 'bgp' +describe 'redistribute bgp'@14: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp)': 'bgp' +execute strict 'redistribute bgp'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp)': 'bgp' +complete 'redistribute bgp'@15: rv==7 + 'bgp' +describe 'redistribute bgp'@15: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp' +execute strict 'redistribute bgp'@16: rv==0, 'redistribute (kernel|connected|static|rip|ripng|ospf|ospf6|isis|bgp)': 'bgp' +complete 'redistribute bgp'@16: rv==7 + 'bgp' +describe 'redistribute bgp'@16: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)' +execute strict 'redistribute bgp'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', '(null)' +complete 'redistribute bgp'@23: rv==7 + 'bgp' +describe 'redistribute bgp'@23: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp)': 'bgp' +execute strict 'redistribute bgp'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp)': 'bgp' +complete 'redistribute bgp'@24: rv==7 + 'bgp' +describe 'redistribute bgp'@24: rv==0 + 'bgp' 'Border Gateway Protocol (BGP)' +execute relaxed 'redistribute bgp m 10'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp) metric <0-16>': 'bgp', '10' +execute strict 'redistribute bgp m 10'@14: rv==2 +complete 'redistribute bgp m 10'@14: rv==2 +describe 'redistribute bgp m 10'@14: rv==0 + '<0-16>' 'Metric value' +execute relaxed 'redistribute bgp m 10'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp) metric <0-16>': 'bgp', '10' +execute strict 'redistribute bgp m 10'@15: rv==2 +complete 'redistribute bgp m 10'@15: rv==2 +describe 'redistribute bgp m 10'@15: rv==0 + '<0-16>' 'Metric value' +execute relaxed 'redistribute bgp m 10'@23: rv==3 +execute strict 'redistribute bgp m 10'@23: rv==2 +complete 'redistribute bgp m 10'@23: rv==3 +describe 'redistribute bgp m 10'@23: rv==3 +execute relaxed 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)' +execute strict 'redistribute bgp metric 10 metric-type 1'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '10', '1', '(null)' +complete 'redistribute bgp metric 10 metric-type 1'@23: rv==7 + '1' +describe 'redistribute bgp metric 10 metric-type 1'@23: rv==0 + '1' 'Set OSPF External Type 1 metrics' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0, 'redistribute (kernel|connected|static|ospf|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@14: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0, 'redistribute (kernel|connected|static|ospf6|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@15: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0, 'redistribute (kernel|connected|static|rip|isis|bgp) {metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'bgp', '(null)', '(null)', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@23: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +execute strict 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0, 'redistribute (kernel|connected|static|ripng|isis|bgp) route-map WORD': 'bgp', 'RMAP_REDIST_BGP' +complete 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==2 +describe 'redistribute bgp route-map RMAP_REDIST_BGP'@24: rv==0 + 'WORD' 'Route map name' +execute relaxed 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)' +execute strict 'default-information originate metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '1', '(null)' +complete 'default-information originate metric-type 1 metric 10'@23: rv==2 +describe 'default-information originate metric-type 1 metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)' +execute strict 'default-information originate always metric-type 1 metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '10', '1', '(null)' +complete 'default-information originate always metric-type 1 metric 10'@23: rv==2 +describe 'default-information originate always metric-type 1 metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT' +execute strict 'default-information originate route-map RMAP_DEFAULT'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '(null)', '(null)', 'RMAP_DEFAULT' +complete 'default-information originate route-map RMAP_DEFAULT'@23: rv==2 +describe 'default-information originate route-map RMAP_DEFAULT'@23: rv==0 + 'WORD' 'Pointer to route-map entries' +execute relaxed 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT' +execute strict 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': '(null)', '10', '(null)', 'RMAP_DEFAULT' +complete 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==2 +describe 'default-information originate route-map RMAP_DEFAULT metric 10'@23: rv==0 + '<0-16777214>' 'OSPF metric' +execute relaxed 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)' +execute strict 'default-information originate always metric-type 2 metric 23'@23: rv==0, 'default-information originate {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}': 'always', '23', '2', '(null)' +complete 'default-information originate always metric-type 2 metric 23'@23: rv==2 +describe 'default-information originate always metric-type 2 metric 23'@23: rv==0 + '<0-16777214>' 'OSPF metric' diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c new file mode 100644 index 0000000..2589fca --- /dev/null +++ b/tests/lib/cxxcompat.c @@ -0,0 +1,123 @@ +/* + * C++ compatibility compile-time smoketest + * Copyright (C) 2019 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define test__cplusplus + +#include "lib/zebra.h" + +#include "lib/agg_table.h" +#include "lib/bfd.h" +#include "lib/bitfield.h" +#include "lib/buffer.h" +#include "lib/checksum.h" +#include "lib/command.h" +#include "lib/command_graph.h" +#include "lib/command_match.h" +#include "lib/compiler.h" +#include "lib/csv.h" +#include "lib/debug.h" +#include "lib/distribute.h" +#include "lib/ferr.h" +#include "lib/filter.h" +#include "lib/frr_pthread.h" +#include "lib/frratomic.h" +#include "lib/frrstr.h" +#include "lib/getopt.h" +#include "lib/graph.h" +#include "lib/hash.h" +#include "lib/hook.h" +#include "lib/id_alloc.h" +#include "lib/if.h" +#include "lib/if_rmap.h" +#include "lib/imsg.h" +#include "lib/ipaddr.h" +#include "lib/jhash.h" +#include "lib/json.h" +#include "lib/keychain.h" +#include "lib/lib_errors.h" +#include "lib/lib_vty.h" +#include "lib/libfrr.h" +#include "lib/libospf.h" +#include "lib/linklist.h" +#include "lib/log.h" +#include "lib/md5.h" +#include "lib/memory.h" +#include "lib/mlag.h" +#include "lib/module.h" +#include "lib/monotime.h" +#include "lib/mpls.h" +#include "lib/network.h" +#include "lib/nexthop.h" +#include "lib/nexthop_group.h" +#include "lib/northbound.h" +#include "lib/northbound_cli.h" +#include "lib/northbound_db.h" +#include "lib/ns.h" +#include "lib/openbsd-tree.h" +#include "lib/pbr.h" +#include "lib/plist.h" +#include "lib/prefix.h" +#include "lib/privs.h" +#include "lib/ptm_lib.h" +#include "lib/pw.h" +#include "lib/qobj.h" +#include "lib/queue.h" +#include "lib/ringbuf.h" +#include "lib/routemap.h" +#include "lib/sbuf.h" +#include "lib/sha256.h" +#include "lib/sigevent.h" +#include "lib/skiplist.h" +#include "lib/sockopt.h" +#include "lib/sockunion.h" +#include "lib/spf_backoff.h" +#include "lib/srcdest_table.h" +#include "lib/stream.h" +#include "lib/table.h" +#include "lib/termtable.h" +#include "lib/thread.h" +#include "lib/typesafe.h" +#include "lib/typerb.h" +#include "lib/vector.h" +#include "lib/vlan.h" +#include "lib/vrf.h" +#include "lib/vty.h" +#include "lib/vxlan.h" +#include "lib/wheel.h" +/* #include "lib/workqueue.h" -- macro problem with STAILQ_LAST */ +#include "lib/yang.h" +#include "lib/yang_translator.h" +#include "lib/yang_wrappers.h" +#include "lib/zclient.h" + +PREDECL_RBTREE_UNIQ(footree); +struct foo { + int dummy; + struct footree_item item; +}; +static int foocmp(const struct foo *a, const struct foo *b) +{ + return memcmp(&a->dummy, &b->dummy, sizeof(a->dummy)); +} +DECLARE_RBTREE_UNIQ(footree, struct foo, item, foocmp); + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/tests/lib/fuzz_zlog.c b/tests/lib/fuzz_zlog.c new file mode 100644 index 0000000..14c8571 --- /dev/null +++ b/tests/lib/fuzz_zlog.c @@ -0,0 +1,130 @@ +/* + * zlog fuzzer target. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "log.h" +#include "zlog_5424.h" +#include "command.h" + +struct input_opts { + uint16_t out1_debug; + uint16_t out2_debug; + uint16_t out3_warn; + uint8_t fmt; + uint8_t dst; +}; + +static char buffer[65536]; + +int main(int argc, char **argv) +{ + struct input_opts io; + int fd; + int pair[2] = {-1, -1}; + + if (read(0, &io, sizeof(io)) != sizeof(io)) + return 1; + if (io.fmt > ZLOG_FMT_LAST) + return 1; + + switch (io.dst) { + case 0: + fd = 1; + break; + case 1: + socketpair(AF_UNIX, SOCK_STREAM, 0, pair); + fd = pair[0]; + break; + case 2: + socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair); + fd = pair[0]; + break; + case 3: + socketpair(AF_UNIX, SOCK_DGRAM, 0, pair); + fd = pair[0]; + break; + default: + return 1; + } + + pid_t child = -1; + + if (pair[1] != -1) { + child = fork(); + + if (child == 0) { + char buf[4096]; + + close(pair[0]); + + while (read(pair[1], buf, sizeof(buf)) > 0) + ; + exit(0); + } else if (child == -1) { + perror("fork"); + return 1; + } + close(pair[1]); + } + + for (size_t i = 0; i < sizeof(buffer); i++) + buffer[i] = (i | 0x20) & 0x7f; + + zlog_aux_init("FUZZBALL: ", LOG_DEBUG); + zlog_tls_buffer_init(); + + struct zlog_cfg_5424 cfg[1] = {}; + + zlog_5424_init(cfg); + + cfg->facility = LOG_DAEMON; + cfg->prio_min = LOG_DEBUG; + cfg->kw_version = true; + cfg->kw_location = true; + cfg->kw_uid = true; + cfg->kw_ec = true; + cfg->kw_args = true; + + cfg->ts_flags = 9; + cfg->fmt = io.fmt; + cfg->dst = ZLOG_5424_DST_FD; + cfg->fd = fd; + + cmd_hostname_set("TEST"); + cfg->master = thread_master_create("TEST"); + + zlog_5424_apply_dst(cfg); + + zlog_debug("test #1 %.*s", (int)io.out1_debug, buffer); + zlog_debug("test #2 %.*s", (int)io.out2_debug, buffer); + zlog_warn("test #1 %.*s", (int)io.out3_warn, buffer); + + zlog_tls_buffer_flush(); + zlog_tls_buffer_fini(); + + /* AFL++ seems to do some weird stuff with its fuzzing target, make + * sure the fork() child is zapped here rather than creating hordes + * of it. + */ + close(fd); + if (child != -1) + kill(child, SIGTERM); + + return 0; +} diff --git a/tests/lib/fuzz_zlog_inputs.py b/tests/lib/fuzz_zlog_inputs.py new file mode 100644 index 0000000..74f3286 --- /dev/null +++ b/tests/lib/fuzz_zlog_inputs.py @@ -0,0 +1,41 @@ +# zlog fuzz-tester input generator +# +# Copyright (C) 2021 David Lamparter for NetDEF, Inc. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; see the file COPYING; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from itertools import chain +import struct + +lengths = set([128]) +# lengths = [[i, i + 1, i + 3, i - 1, i - 3] for i in lengths] +# lengths = set([i for i in chain(*lengths) if i >= 0]) + +dsts = [0, 1, 2, 3] +fmts = [0, 1, 2, 3] + + +def combo(): + for l0 in lengths: + for l1 in lengths: + for l2 in lengths: + for fmt in fmts: + for dst in dsts: + yield (l0, l1, l2, fmt, dst) + + +for i, tup in enumerate(combo()): + with open("input/i%d" % i, "wb") as fd: + fd.write(struct.pack("HHHBB", *tup)) diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c new file mode 100644 index 0000000..08eb0d5 --- /dev/null +++ b/tests/lib/northbound/test_oper_data.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2018 NetDEF, Inc. + * Renato Westphal + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "lib_vty.h" +#include "log.h" +#include "northbound.h" + +static struct thread_master *master; + +struct troute { + struct prefix_ipv4 prefix; + struct in_addr nexthop; + char ifname[IFNAMSIZ]; + uint8_t metric; + bool active; +}; + +struct tvrf { + char name[32]; + struct list *interfaces; + struct list *routes; +}; + +static struct list *vrfs; + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf + */ +static const void * +frr_test_module_vrfs_vrf_get_next(struct nb_cb_get_next_args *args) +{ + struct listnode *node; + + if (args->list_entry == NULL) + node = listhead(vrfs); + else + node = listnextnode((struct listnode *)args->list_entry); + + return node; +} + +static int frr_test_module_vrfs_vrf_get_keys(struct nb_cb_get_keys_args *args) +{ + const struct tvrf *vrf; + + vrf = listgetdata((struct listnode *)args->list_entry); + + args->keys->num = 1; + strlcpy(args->keys->key[0], vrf->name, sizeof(args->keys->key[0])); + + return NB_OK; +} + +static const void * +frr_test_module_vrfs_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args) +{ + struct listnode *node; + struct tvrf *vrf; + const char *vrfname; + + vrfname = args->keys->key[0]; + + for (ALL_LIST_ELEMENTS_RO(vrfs, node, vrf)) { + if (strmatch(vrf->name, vrfname)) + return node; + } + + return NULL; +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/name + */ +static struct yang_data * +frr_test_module_vrfs_vrf_name_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct tvrf *vrf; + + vrf = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_string(args->xpath, vrf->name); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface + */ +static struct yang_data *frr_test_module_vrfs_vrf_interfaces_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const char *interface; + + interface = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_string(args->xpath, interface); +} + +static const void *frr_test_module_vrfs_vrf_interfaces_interface_get_next( + struct nb_cb_get_next_args *args) +{ + const struct tvrf *vrf; + struct listnode *node; + + vrf = listgetdata((struct listnode *)args->parent_list_entry); + if (args->list_entry == NULL) + node = listhead(vrf->interfaces); + else + node = listnextnode((struct listnode *)args->list_entry); + + return node; +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route + */ +static const void * +frr_test_module_vrfs_vrf_routes_route_get_next(struct nb_cb_get_next_args *args) +{ + const struct tvrf *vrf; + struct listnode *node; + + vrf = listgetdata((struct listnode *)args->parent_list_entry); + if (args->list_entry == NULL) + node = listhead(vrf->routes); + else + node = listnextnode((struct listnode *)args->list_entry); + + return node; +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix + */ +static struct yang_data *frr_test_module_vrfs_vrf_routes_route_prefix_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_ipv4p(args->xpath, &route->prefix); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop + */ +static struct yang_data * +frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_ipv4(args->xpath, &route->nexthop); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface + */ +static struct yang_data * +frr_test_module_vrfs_vrf_routes_route_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_string(args->xpath, route->ifname); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric + */ +static struct yang_data *frr_test_module_vrfs_vrf_routes_route_metric_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)args->list_entry); + return yang_data_new_uint8(args->xpath, route->metric); +} + +/* + * XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/active + */ +static struct yang_data *frr_test_module_vrfs_vrf_routes_route_active_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct troute *route; + + route = listgetdata((struct listnode *)args->list_entry); + if (route->active) + return yang_data_new(args->xpath, NULL); + + return NULL; +} + +/* clang-format off */ +const struct frr_yang_module_info frr_test_module_info = { + .name = "frr-test-module", + .nodes = { + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf", + .cbs.get_next = frr_test_module_vrfs_vrf_get_next, + .cbs.get_keys = frr_test_module_vrfs_vrf_get_keys, + .cbs.lookup_entry = frr_test_module_vrfs_vrf_lookup_entry, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/name", + .cbs.get_elem = frr_test_module_vrfs_vrf_name_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface", + .cbs.get_elem = frr_test_module_vrfs_vrf_interfaces_interface_get_elem, + .cbs.get_next = frr_test_module_vrfs_vrf_interfaces_interface_get_next, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route", + .cbs.get_next = frr_test_module_vrfs_vrf_routes_route_get_next, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_prefix_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_interface_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_metric_get_elem, + }, + { + .xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/active", + .cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem, + }, + { + .xpath = NULL, + }, + } +}; +/* clang-format on */ + +static const struct frr_yang_module_info *const modules[] = { + &frr_test_module_info, +}; + +static void create_data(unsigned int num_vrfs, unsigned int num_interfaces, + unsigned int num_routes) +{ + struct prefix_ipv4 base_prefix; + struct in_addr base_nexthop; + + (void)str2prefix_ipv4("10.0.0.0/32", &base_prefix); + (void)inet_pton(AF_INET, "172.16.0.0", &base_nexthop); + + vrfs = list_new(); + + /* Create VRFs. */ + for (unsigned int i = 0; i < num_vrfs; i++) { + struct tvrf *vrf; + + vrf = XCALLOC(MTYPE_TMP, sizeof(*vrf)); + snprintf(vrf->name, sizeof(vrf->name), "vrf%u", i); + vrf->interfaces = list_new(); + vrf->routes = list_new(); + + /* Create interfaces. */ + for (unsigned int j = 0; j < num_interfaces; j++) { + char ifname[32]; + char *interface; + + snprintf(ifname, sizeof(ifname), "eth%u", j); + interface = XSTRDUP(MTYPE_TMP, ifname); + listnode_add(vrf->interfaces, interface); + } + + /* Create routes. */ + for (unsigned int j = 0; j < num_routes; j++) { + struct troute *route; + + route = XCALLOC(MTYPE_TMP, sizeof(*route)); + + memcpy(&route->prefix, &base_prefix, + sizeof(route->prefix)); + route->prefix.prefix.s_addr = + htonl(ntohl(route->prefix.prefix.s_addr) + j); + + memcpy(&route->nexthop, &base_nexthop, + sizeof(route->nexthop)); + route->nexthop.s_addr = + htonl(ntohl(route->nexthop.s_addr) + j); + + snprintf(route->ifname, sizeof(route->ifname), "eth%u", + j); + route->metric = j % 256; + route->active = (j % 2 == 0); + listnode_add(vrf->routes, route); + } + + listnode_add(vrfs, vrf); + } +} + +static void interface_delete(void *ptr) +{ + char *interface = ptr; + + XFREE(MTYPE_TMP, interface); +} + +static void route_delete(void *ptr) +{ + struct troute *route = ptr; + + XFREE(MTYPE_TMP, route); +} + +static void vrf_delete(void *ptr) +{ + struct tvrf *vrf = ptr; + + vrf->interfaces->del = interface_delete; + list_delete(&vrf->interfaces); + vrf->routes->del = route_delete; + list_delete(&vrf->routes); + XFREE(MTYPE_TMP, vrf); +} + +static void delete_data(void) +{ + vrfs->del = vrf_delete; + list_delete(&vrfs); +} + +static void vty_do_exit(int isexit) +{ + printf("\nend.\n"); + + delete_data(); + + cmd_terminate(); + vty_terminate(); + nb_terminate(); + yang_terminate(); + thread_master_free(master); + + log_memstats(stderr, "test-nb-oper-data"); + if (!isexit) + exit(0); +} + +/* main routine. */ +int main(int argc, char **argv) +{ + struct thread thread; + unsigned int num_vrfs = 2; + unsigned int num_interfaces = 4; + unsigned int num_routes = 6; + + if (argc > 1) + num_vrfs = atoi(argv[1]); + if (argc > 2) + num_interfaces = atoi(argv[2]); + if (argc > 3) + num_routes = atoi(argv[3]); + + /* Set umask before anything for security */ + umask(0027); + + /* master init. */ + master = thread_master_create(NULL); + + zlog_aux_init("NONE: ", ZLOG_DISABLED); + + /* Library inits. */ + cmd_init(1); + cmd_hostname_set("test"); + vty_init(master, false); + lib_cmd_init(); + nb_init(master, modules, array_size(modules), false); + + /* Create artificial data. */ + create_data(num_vrfs, num_interfaces, num_routes); + + /* Read input from .in file. */ + vty_stdio(vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); + + /* Not reached. */ + exit(0); +} diff --git a/tests/lib/northbound/test_oper_data.in b/tests/lib/northbound/test_oper_data.in new file mode 100644 index 0000000..a6c4f87 --- /dev/null +++ b/tests/lib/northbound/test_oper_data.in @@ -0,0 +1 @@ +show yang operational-data /frr-test-module:frr-test-module diff --git a/tests/lib/northbound/test_oper_data.py b/tests/lib/northbound/test_oper_data.py new file mode 100644 index 0000000..a02bf05 --- /dev/null +++ b/tests/lib/northbound/test_oper_data.py @@ -0,0 +1,5 @@ +import frrtest + + +class TestNbOperData(frrtest.TestRefOut): + program = "./test_oper_data" diff --git a/tests/lib/northbound/test_oper_data.refout b/tests/lib/northbound/test_oper_data.refout new file mode 100644 index 0000000..57ecd2f --- /dev/null +++ b/tests/lib/northbound/test_oper_data.refout @@ -0,0 +1,119 @@ +test# show yang operational-data /frr-test-module:frr-test-module
+{
+ "frr-test-module:frr-test-module": {
+ "vrfs": {
+ "vrf": [
+ {
+ "name": "vrf0",
+ "interfaces": {
+ "interface": [
+ "eth0",
+ "eth1",
+ "eth2",
+ "eth3"
+ ]
+ },
+ "routes": {
+ "route": [
+ {
+ "prefix": "10.0.0.0/32",
+ "next-hop": "172.16.0.0",
+ "interface": "eth0",
+ "metric": 0,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.1/32",
+ "next-hop": "172.16.0.1",
+ "interface": "eth1",
+ "metric": 1
+ },
+ {
+ "prefix": "10.0.0.2/32",
+ "next-hop": "172.16.0.2",
+ "interface": "eth2",
+ "metric": 2,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.3/32",
+ "next-hop": "172.16.0.3",
+ "interface": "eth3",
+ "metric": 3
+ },
+ {
+ "prefix": "10.0.0.4/32",
+ "next-hop": "172.16.0.4",
+ "interface": "eth4",
+ "metric": 4,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.5/32",
+ "next-hop": "172.16.0.5",
+ "interface": "eth5",
+ "metric": 5
+ }
+ ]
+ }
+ },
+ {
+ "name": "vrf1",
+ "interfaces": {
+ "interface": [
+ "eth0",
+ "eth1",
+ "eth2",
+ "eth3"
+ ]
+ },
+ "routes": {
+ "route": [
+ {
+ "prefix": "10.0.0.0/32",
+ "next-hop": "172.16.0.0",
+ "interface": "eth0",
+ "metric": 0,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.1/32",
+ "next-hop": "172.16.0.1",
+ "interface": "eth1",
+ "metric": 1
+ },
+ {
+ "prefix": "10.0.0.2/32",
+ "next-hop": "172.16.0.2",
+ "interface": "eth2",
+ "metric": 2,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.3/32",
+ "next-hop": "172.16.0.3",
+ "interface": "eth3",
+ "metric": 3
+ },
+ {
+ "prefix": "10.0.0.4/32",
+ "next-hop": "172.16.0.4",
+ "interface": "eth4",
+ "metric": 4,
+ "active": [null]
+ },
+ {
+ "prefix": "10.0.0.5/32",
+ "next-hop": "172.16.0.5",
+ "interface": "eth5",
+ "metric": 5
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+}
+test# +end. diff --git a/tests/lib/script1.lua b/tests/lib/script1.lua new file mode 100644 index 0000000..6361c96 --- /dev/null +++ b/tests/lib/script1.lua @@ -0,0 +1,54 @@ + +-- Positive testing + +function foo(a, b) + a = a + 1 + b = b + 1 + return { + a = a, + b = b, + } +end + +function bar(a, b) + a = a + 1 + b = b + 1 + c = 303 + return { + b = b, + c = c, + } +end + +function fact(n) + -- outer function must return a table + -- inner functions can be used to recurse or as helpers + function helper(m) + if m == 0 then + return 1 + else + return m * helper(m - 1) + end + end + return { + ans = helper(n) + } +end + +-- Negative testing + +function bad_return1() +end + +function bad_return2() + return 123 +end + +function bad_return3() + return {} +end + +function bad_return4() + error("Something bad!") +end + diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am new file mode 100644 index 0000000..62b1944 --- /dev/null +++ b/tests/lib/subdir.am @@ -0,0 +1,372 @@ +############################################################################## +if SCRIPTING +check_PROGRAMS += tests/lib/test_frrlua +endif +tests_lib_test_frrlua_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_frrlua_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_frrlua_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_frrlua_SOURCES = tests/lib/test_frrlua.c +EXTRA_DIST += tests/lib/test_frrlua.py + +if SCRIPTING +check_PROGRAMS += tests/lib/test_frrscript +endif +tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c +EXTRA_DIST += tests/lib/test_frrscript.py + + +############################################################################## +GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm + +if GRPC +check_PROGRAMS += tests/lib/test_grpc +endif +tests_lib_test_grpc_CXXFLAGS = $(WERROR) $(TESTS_CXXFLAGS) +tests_lib_test_grpc_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_grpc_LDADD = $(GRPC_TESTS_LDADD) +tests_lib_test_grpc_SOURCES = tests/lib/test_grpc.cpp + + +############################################################################## +if ZEROMQ +check_PROGRAMS += tests/lib/test_zmq +endif +tests_lib_test_zmq_CFLAGS = $(TESTS_CFLAGS) $(ZEROMQ_CFLAGS) +tests_lib_test_zmq_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_zmq_LDADD = lib/libfrrzmq.la $(ALL_TESTS_LDADD) $(ZEROMQ_LIBS) +tests_lib_test_zmq_SOURCES = tests/lib/test_zmq.c + + +############################################################################## +if CARES +check_PROGRAMS += tests/lib/test_resolver +endif +tests_lib_test_resolver_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_resolver_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_resolver_LDADD = $(ALL_TESTS_LDADD) lib/libfrrcares.la +tests_lib_test_resolver_SOURCES = tests/lib/test_resolver.c tests/lib/cli/common_cli.c + + +############################################################################## +noinst_HEADERS += \ + tests/helpers/c/prng.h \ + tests/helpers/c/tests.h \ + tests/lib/cli/common_cli.h \ + # end + + +check_PROGRAMS += tests/lib/cxxcompat +tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR) +tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_cxxcompat_SOURCES = tests/lib/cxxcompat.c +tests_lib_cxxcompat_LDADD = $(ALL_TESTS_LDADD) + + +check_PROGRAMS += tests/lib/fuzz_zlog +tests_lib_fuzz_zlog_CFLAGS = $(TESTS_CFLAGS) +tests_lib_fuzz_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_fuzz_zlog_LDADD = $(ALL_TESTS_LDADD) +tests_lib_fuzz_zlog_SOURCES = tests/lib/fuzz_zlog.c +EXTRA_DIST += tests/lib/fuzz_zlog_inputs.py + + +check_PROGRAMS += tests/lib/cli/test_cli +tests_lib_cli_test_cli_CFLAGS = $(TESTS_CFLAGS) +tests_lib_cli_test_cli_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD) +tests_lib_cli_test_cli_SOURCES = tests/lib/cli/test_cli.c tests/lib/cli/common_cli.c +clippy_scan += tests/lib/cli/test_cli.c +EXTRA_DIST += \ + tests/lib/cli/test_cli.in \ + tests/lib/cli/test_cli.py \ + tests/lib/cli/test_cli.refout \ + # end + + +check_PROGRAMS += tests/lib/cli/test_commands +tests_lib_cli_test_commands_CFLAGS = $(TESTS_CFLAGS) +tests_lib_cli_test_commands_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) +nodist_tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands_defun.c +tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands.c tests/helpers/c/prng.c +tests/lib/cli/test_commands_defun.c: vtysh/vtysh_cmd.c + @mkdir -p tests/lib/cli + $(AM_V_GEN)sed \ + -e 's%"vtysh/vtysh\.h"%"tests/helpers/c/tests.h"%' \ + -e 's/vtysh_init_cmd/test_init_cmd/' \ + -e 's/VTYSH_[A-Z][A-Z_0-9]*/0/g' \ + < vtysh/vtysh_cmd.c \ + > "$@" +CLEANFILES += tests/lib/cli/test_commands_defun.c +EXTRA_DIST += \ + tests/lib/cli/test_commands.in \ + tests/lib/cli/test_commands.py \ + tests/lib/cli/test_commands.refout \ + # end + + +check_PROGRAMS += tests/lib/northbound/test_oper_data +tests_lib_northbound_test_oper_data_CFLAGS = $(TESTS_CFLAGS) +tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD) +tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c +nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c +EXTRA_DIST += \ + tests/lib/northbound/test_oper_data.in \ + tests/lib/northbound/test_oper_data.py \ + tests/lib/northbound/test_oper_data.refout \ + # end + + +check_PROGRAMS += tests/lib/test_assert +tests_lib_test_assert_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_assert_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_assert_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_assert_SOURCES = tests/lib/test_assert.c +EXTRA_DIST += tests/lib/test_assert.py + + +check_PROGRAMS += tests/lib/test_atomlist +tests_lib_test_atomlist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_atomlist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_atomlist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_atomlist_SOURCES = tests/lib/test_atomlist.c +EXTRA_DIST += tests/lib/test_atomlist.py + + +check_PROGRAMS += tests/lib/test_buffer +tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_buffer_SOURCES = tests/lib/test_buffer.c + + +check_PROGRAMS += tests/lib/test_checksum +tests_lib_test_checksum_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_checksum_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_checksum_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_checksum_SOURCES = tests/lib/test_checksum.c tests/helpers/c/prng.c + + +check_PROGRAMS += tests/lib/test_graph +tests_lib_test_graph_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_graph_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_graph_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_graph_SOURCES = tests/lib/test_graph.c +EXTRA_DIST += \ + tests/lib/test_graph.py \ + tests/lib/test_graph.refout \ + # end + + +check_PROGRAMS += tests/lib/test_heavy +tests_lib_test_heavy_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_heavy_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_heavy_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_heavy_SOURCES = tests/lib/test_heavy.c tests/helpers/c/main.c + + +check_PROGRAMS += tests/lib/test_heavy_thread +tests_lib_test_heavy_thread_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_heavy_thread_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_heavy_thread_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_heavy_thread_SOURCES = tests/lib/test_heavy_thread.c tests/helpers/c/main.c + + +check_PROGRAMS += tests/lib/test_heavy_wq +tests_lib_test_heavy_wq_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_heavy_wq_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_heavy_wq_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_heavy_wq_SOURCES = tests/lib/test_heavy_wq.c tests/helpers/c/main.c + + +check_PROGRAMS += tests/lib/test_idalloc +tests_lib_test_idalloc_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_idalloc_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_idalloc_SOURCES = tests/lib/test_idalloc.c + + +check_PROGRAMS += tests/lib/test_memory +tests_lib_test_memory_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_memory_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_memory_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_memory_SOURCES = tests/lib/test_memory.c + + +check_PROGRAMS += tests/lib/test_nexthop_iter +tests_lib_test_nexthop_iter_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_nexthop_iter_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_nexthop_iter_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_nexthop_iter_SOURCES = tests/lib/test_nexthop_iter.c tests/helpers/c/prng.c +EXTRA_DIST += tests/lib/test_nexthop_iter.py + + +check_PROGRAMS += tests/lib/test_nexthop +tests_lib_test_nexthop_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_nexthop_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_nexthop_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_nexthop_SOURCES = tests/lib/test_nexthop.c +EXTRA_DIST += tests/lib/test_nexthop.py + + +check_PROGRAMS += tests/lib/test_ntop +tests_lib_test_ntop_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_ntop_CPPFLAGS = $(CPPFLAGS_BASE) # no assert override +tests_lib_test_ntop_LDADD = # none +tests_lib_test_ntop_SOURCES = tests/lib/test_ntop.c tests/helpers/c/prng.c +EXTRA_DIST += tests/lib/test_ntop.py + + +check_PROGRAMS += tests/lib/test_plist +tests_lib_test_plist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_plist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_plist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_plist_SOURCES = tests/lib/test_plist.c tests/lib/cli/common_cli.c + + +check_PROGRAMS += tests/lib/test_prefix2str +tests_lib_test_prefix2str_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_prefix2str_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_prefix2str_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_prefix2str_SOURCES = tests/lib/test_prefix2str.c tests/helpers/c/prng.c +EXTRA_DIST += tests/lib/test_prefix2str.py + + +check_PROGRAMS += tests/lib/test_printfrr +tests_lib_test_printfrr_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_printfrr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_printfrr_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_printfrr_SOURCES = tests/lib/test_printfrr.c +EXTRA_DIST += tests/lib/test_printfrr.py + + +check_PROGRAMS += tests/lib/test_privs +tests_lib_test_privs_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_privs_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_privs_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_privs_SOURCES = tests/lib/test_privs.c + + +check_PROGRAMS += tests/lib/test_ringbuf +tests_lib_test_ringbuf_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_ringbuf_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_ringbuf_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_ringbuf_SOURCES = tests/lib/test_ringbuf.c +EXTRA_DIST += tests/lib/test_ringbuf.py + + +check_PROGRAMS += tests/lib/test_segv +tests_lib_test_segv_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_segv_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_segv_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_segv_SOURCES = tests/lib/test_segv.c + + +check_PROGRAMS += tests/lib/test_seqlock +tests_lib_test_seqlock_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_seqlock_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_seqlock_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_seqlock_SOURCES = tests/lib/test_seqlock.c + + +check_PROGRAMS += tests/lib/test_sig +tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_sig_SOURCES = tests/lib/test_sig.c + + +check_PROGRAMS += tests/lib/test_skiplist +tests_lib_test_skiplist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_skiplist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_skiplist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_skiplist_SOURCES = tests/lib/test_skiplist.c + + +check_PROGRAMS += tests/lib/test_srcdest_table +tests_lib_test_srcdest_table_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_srcdest_table_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_srcdest_table_SOURCES = tests/lib/test_srcdest_table.c tests/helpers/c/prng.c +EXTRA_DIST += tests/lib/test_srcdest_table.py + + +check_PROGRAMS += tests/lib/test_stream +tests_lib_test_stream_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_stream_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_stream_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_stream_SOURCES = tests/lib/test_stream.c +EXTRA_DIST += \ + tests/lib/test_stream.py \ + tests/lib/test_stream.refout \ + # end + + +check_PROGRAMS += tests/lib/test_table +tests_lib_test_table_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_table_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm +tests_lib_test_table_SOURCES = tests/lib/test_table.c +EXTRA_DIST += tests/lib/test_table.py + + +check_PROGRAMS += tests/lib/test_timer_correctness +tests_lib_test_timer_correctness_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_timer_correctness_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_timer_correctness_SOURCES = tests/lib/test_timer_correctness.c tests/helpers/c/prng.c +EXTRA_DIST += tests/lib/test_timer_correctness.py + + +check_PROGRAMS += tests/lib/test_timer_performance +tests_lib_test_timer_performance_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_timer_performance_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_timer_performance_SOURCES = tests/lib/test_timer_performance.c tests/helpers/c/prng.c + + +check_PROGRAMS += tests/lib/test_ttable +tests_lib_test_ttable_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_ttable_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_ttable_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_ttable_SOURCES = tests/lib/test_ttable.c +EXTRA_DIST += \ + tests/lib/test_ttable.py \ + tests/lib/test_ttable.refout \ + # end + + +check_PROGRAMS += tests/lib/test_typelist +tests_lib_test_typelist_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_typelist_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_typelist_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_typelist_SOURCES = tests/lib/test_typelist.c tests/helpers/c/prng.c +noinst_HEADERS += tests/lib/test_typelist.h +EXTRA_DIST += tests/lib/test_typelist.py + + +check_PROGRAMS += tests/lib/test_versioncmp +tests_lib_test_versioncmp_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_versioncmp_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_versioncmp_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_versioncmp_SOURCES = tests/lib/test_versioncmp.c +EXTRA_DIST += tests/lib/test_versioncmp.py + + +check_PROGRAMS += tests/lib/test_xref +tests_lib_test_xref_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_xref_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_xref_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_xref_SOURCES = tests/lib/test_xref.c +EXTRA_DIST += tests/lib/test_xref.py + + +check_PROGRAMS += tests/lib/test_zlog +tests_lib_test_zlog_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_zlog_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_zlog_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_zlog_SOURCES = tests/lib/test_zlog.c +EXTRA_DIST += tests/lib/test_zlog.py diff --git a/tests/lib/test_assert.c b/tests/lib/test_assert.c new file mode 100644 index 0000000..a453565 --- /dev/null +++ b/tests/lib/test_assert.c @@ -0,0 +1,64 @@ +/* + * Quick test for assert() + * Copyright (C) 2021 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* make sure this works with assert.h & nothing else. also check the include + * shadowing, we don't want to pick up system assert.h + */ +#include <assert.h> + +__attribute__((noinline)) +static void func_for_bt(int number) +{ + assert(number > 2); + assertf(number > 3, "(A) the number was %d", number); +} + +#include <zebra.h> +#include "lib/zlog.h" +#include "lib/thread.h" +#include "lib/sigevent.h" + +int main(int argc, char **argv) +{ + int number = 10; + struct thread_master *master; + + zlog_aux_init("NONE: ", LOG_DEBUG); + + if (argc > 1) + number = atoi(argv[1]); + + assert(number > 0); + assertf(number > 1, "(B) the number was %d", number); + + /* set up SIGABRT handler */ + master = thread_master_create("test"); + signal_init(master, 0, NULL); + + func_for_bt(number); + assert(number > 4); + assertf(number > 5, "(C) the number was %d", number); + + assertf(number > 10, "(D) the number was %d", number); + return 0; +} diff --git a/tests/lib/test_assert.py b/tests/lib/test_assert.py new file mode 100644 index 0000000..67c88e6 --- /dev/null +++ b/tests/lib/test_assert.py @@ -0,0 +1,56 @@ +import frrtest +import os +import re +import subprocess +import inspect + +basedir = os.path.dirname(__file__) +program = os.path.join(basedir, "test_assert") + + +def check(number, rex=None): + proc = subprocess.Popen( + [frrtest.binpath(program), str(number)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + out, err = proc.communicate() + exitcode = proc.wait() + + if rex is None: + assert exitcode == 0 + else: + assert exitcode != 0 + + text = out.decode("US-ASCII") + err.decode("US-ASCII") + rex = re.compile(rex, re.M | re.S) + m = rex.search(text) + assert m is not None, "non-matching output: %s" % text + + +def test_assert_0(): + check(0, r"test_assert\.c:\d+.*number > 0") + + +def test_assert_1(): + check(1, r"test_assert\.c:\d+.*number > 1.*\(B\) the number was 1") + + +def test_assert_2(): + check(2, r"test_assert\.c:\d+.*number > 2") + + +def test_assert_3(): + check(3, r"test_assert\.c:\d+.*number > 3.*\(A\) the number was 3") + + +def test_assert_4(): + check(4, r"test_assert\.c:\d+.*number > 4") + + +def test_assert_10(): + check(10, r"test_assert\.c:\d+.*number > 10.*\(D\) the number was 10") + + +def test_assert_11(): + check(11) diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c new file mode 100644 index 0000000..83dd9f2 --- /dev/null +++ b/tests/lib/test_atomlist.c @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include <pthread.h> + +#include "atomlist.h" +#include "seqlock.h" +#include "monotime.h" +#include "printfrr.h" + +/* + * maybe test: + * - alist_del_hint + * - alist_next_safe + * - asort_del_hint + * - asort_next_safe + */ + +static struct seqlock sqlo; + +PREDECL_ATOMLIST(alist); +PREDECL_ATOMSORT_UNIQ(asort); +struct item { + uint64_t val1; + struct alist_item chain; + struct asort_item sortc; + uint64_t val2; +}; +DECLARE_ATOMLIST(alist, struct item, chain); + +static int icmp(const struct item *a, const struct item *b); +DECLARE_ATOMSORT_UNIQ(asort, struct item, sortc, icmp); + +static int icmp(const struct item *a, const struct item *b) +{ + if (a->val1 > b->val1) + return 1; + if (a->val1 < b->val1) + return -1; + return 0; +} + +#define NITEM 10000 +struct item itm[NITEM]; + +static struct alist_head ahead; +static struct asort_head shead; + +#define NTHREADS 4 +static struct testthread { + pthread_t pt; + struct seqlock sqlo; + size_t counter, nullops; +} thr[NTHREADS]; + +struct testrun { + struct testrun *next; + int lineno; + const char *desc; + ssize_t prefill; + bool sorted; + void (*func)(unsigned int offset); +}; +struct testrun *runs = NULL; + +#define NOCLEAR -1 + +#define deftestrun(name, _desc, _prefill, _sorted) \ +static void trfunc_##name(unsigned int offset); \ +struct testrun tr_##name = { \ + .desc = _desc, \ + .lineno = __LINE__, \ + .prefill = _prefill, \ + .func = &trfunc_##name, \ + .sorted = _sorted }; \ +static void __attribute__((constructor)) trsetup_##name(void) \ +{ \ + struct testrun **inspos = &runs; \ + while (*inspos && (*inspos)->lineno < tr_##name.lineno) \ + inspos = &(*inspos)->next; \ + tr_##name.next = *inspos; \ + *inspos = &tr_##name; \ +} \ +static void trfunc_##name(unsigned int offset) \ +{ \ + size_t i = 0, n = 0; + +#define endtestrun \ + thr[offset].counter = i; \ + thr[offset].nullops = n; \ +} + +deftestrun(add, "add vs. add", 0, false) + for (; i < NITEM / NTHREADS; i++) + alist_add_head(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(del, "del vs. del", NOCLEAR, false) + for (; i < NITEM / NTHREADS / 10; i++) + alist_del(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(addtail, "add_tail vs. add_tail", 0, false) + for (; i < NITEM / NTHREADS; i++) + alist_add_tail(&ahead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(pop, "pop vs. pop", NOCLEAR, false) + for (; i < NITEM / NTHREADS; ) + if (alist_pop(&ahead)) + i++; + else + n++; +endtestrun + +deftestrun(headN_vs_pop1, "add_head(N) vs. pop(1)", 1, false); + if (offset == 0) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(head1_vs_popN, "add_head(1) vs. pop(N)", 0, false); + if (offset < NTHREADS - 1) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = 0; i < NITEM; i++) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(headN_vs_popN, "add_head(N) vs. pop(N)", NTHREADS / 2, false) + if (offset < NTHREADS / 2) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM * 2 / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_head(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(tailN_vs_pop1, "add_tail(N) vs. pop(1)", 1, false) + if (offset == 0) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM - (NITEM / NTHREADS); ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = offset; i < NITEM; i += NTHREADS) + alist_add_tail(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(tail1_vs_popN, "add_tail(1) vs. pop(N)", 0, false) + if (offset < NTHREADS - 1) { + struct item *dr = NULL; + + for (i = n = 0; i < NITEM / NTHREADS; ) { + dr = alist_pop(&ahead); + if (dr) + i++; + else + n++; + } + } else { + for (i = 0; i < NITEM; i++) + alist_add_tail(&ahead, &itm[i]); + i = 0; + } +endtestrun + +deftestrun(sort_add, "add_sort vs. add_sort", 0, true) + for (; i < NITEM / NTHREADS / 10; i++) + asort_add(&shead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(sort_del, "del_sort vs. del_sort", NOCLEAR, true) + for (; i < NITEM / NTHREADS / 10; i++) + asort_del(&shead, &itm[i * NTHREADS + offset]); +endtestrun + +deftestrun(sort_add_del, "add_sort vs. del_sort", NTHREADS / 2, true) + if (offset < NTHREADS / 2) { + for (; i < NITEM / NTHREADS / 10; i++) + asort_del(&shead, &itm[i * NTHREADS + offset]); + } else { + for (; i < NITEM / NTHREADS / 10; i++) + asort_add(&shead, &itm[i * NTHREADS + offset]); + } +endtestrun + +static void *thr1func(void *arg) +{ + struct testthread *p = arg; + unsigned int offset = (unsigned int)(p - &thr[0]); + seqlock_val_t sv; + struct testrun *tr; + + for (tr = runs; tr; tr = tr->next) { + sv = seqlock_bump(&p->sqlo) - SEQLOCK_INCR; + seqlock_wait(&sqlo, sv); + + tr->func(offset); + } + seqlock_bump(&p->sqlo); + + return NULL; +} + +static void clear_list(size_t prefill) +{ + size_t i; + + memset(&ahead, 0, sizeof(ahead)); + memset(&shead, 0, sizeof(shead)); + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) { + itm[i].val1 = itm[i].val2 = i; + if ((i % NTHREADS) < prefill) { + alist_add_tail(&ahead, &itm[i]); + asort_add(&shead, &itm[i]); + } + } +} + +static void run_tr(struct testrun *tr) +{ + const char *desc = tr->desc; + struct timeval tv; + int64_t delta; + seqlock_val_t sv; + size_t c = 0, s = 0, n = 0; + struct item *item, *prev, dummy; + + printfrr("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 2, "", desc); + fflush(stdout); + + if (tr->prefill != NOCLEAR) + clear_list(tr->prefill); + + monotime(&tv); + sv = seqlock_bump(&sqlo) - SEQLOCK_INCR; + for (size_t i = 0; i < NTHREADS; i++) { + seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo)); + s += thr[i].counter; + n += thr[i].nullops; + thr[i].counter = 0; + thr[i].nullops = 0; + } + + delta = monotime_since(&tv, NULL); + if (tr->sorted) { + uint64_t prevval = 0; + + frr_each(asort, &shead, item) { + assert(item->val1 >= prevval); + prevval = item->val1; + c++; + } + assert(c == asort_count(&shead)); + } else { + prev = &dummy; + frr_each(alist, &ahead, item) { + assert(item != prev); + prev = item; + c++; + assert(c <= NITEM); + } + assert(c == alist_count(&ahead)); + } + printfrr("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n", + sv >> 2, delta, c, s, n, desc); +} + +#ifdef BASIC_TESTS +static void dump(const char *lbl) +{ + struct item *item, *safe; + size_t ctr = 0; + + printfrr("dumping %s:\n", lbl); + frr_each_safe(alist, &ahead, item) { + printfrr("%s %3zu %p %3"PRIu64" %3"PRIu64"\n", lbl, ctr++, + (void *)item, item->val1, item->val2); + } +} + +static void basic_tests(void) +{ + size_t i; + + memset(&ahead, 0, sizeof(ahead)); + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) + itm[i].val1 = itm[i].val2 = i; + + assert(alist_first(&ahead) == NULL); + dump(""); + alist_add_head(&ahead, &itm[0]); + dump(""); + alist_add_head(&ahead, &itm[1]); + dump(""); + alist_add_tail(&ahead, &itm[2]); + dump(""); + alist_add_tail(&ahead, &itm[3]); + dump(""); + alist_del(&ahead, &itm[1]); + dump(""); + printfrr("POP: %p\n", alist_pop(&ahead)); + dump(""); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + printfrr("POP: %p\n", alist_pop(&ahead)); + dump(""); +} +#else +#define basic_tests() do { } while (0) +#endif + +int main(int argc, char **argv) +{ + size_t i; + + basic_tests(); + + seqlock_init(&sqlo); + seqlock_acquire_val(&sqlo, SEQLOCK_STARTVAL); + + for (i = 0; i < NTHREADS; i++) { + seqlock_init(&thr[i].sqlo); + seqlock_acquire(&thr[i].sqlo, &sqlo); + thr[i].counter = 0; + thr[i].nullops = 0; + + pthread_create(&thr[i].pt, NULL, thr1func, &thr[i]); + } + + struct testrun *tr; + + for (tr = runs; tr; tr = tr->next) + run_tr(tr); + + for (i = 0; i < NTHREADS; i++) + pthread_join(thr[i].pt, NULL); + + return 0; +} diff --git a/tests/lib/test_atomlist.py b/tests/lib/test_atomlist.py new file mode 100644 index 0000000..719a2e7 --- /dev/null +++ b/tests/lib/test_atomlist.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestAtomlist(frrtest.TestMultiOut): + program = "./test_atomlist" + + +TestAtomlist.exit_cleanly() diff --git a/tests/lib/test_buffer.c b/tests/lib/test_buffer.c new file mode 100644 index 0000000..7fb9a76 --- /dev/null +++ b/tests/lib/test_buffer.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2004 Paul Jakma + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <memory.h> +#include <lib_vty.h> +#include <buffer.h> + +struct thread_master *master; + +int main(int argc, char **argv) +{ + struct buffer *b1, *b2; + int n; + char junk[3]; + char c = 'a'; + + lib_cmd_init(); + + if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) { + fprintf(stderr, "Usage: %s <number of chars to simulate>\n", + *argv); + return 1; + } + + b1 = buffer_new(0); + b2 = buffer_new(1024); + + while (n-- > 0) { + buffer_put(b1, &c, 1); + buffer_put(b2, &c, 1); + if (c++ == 'z') + c = 'a'; + buffer_reset(b1); + buffer_reset(b2); + } + buffer_free(b1); + buffer_free(b2); + return 0; +} diff --git a/tests/lib/test_checksum.c b/tests/lib/test_checksum.c new file mode 100644 index 0000000..9a7f2b1 --- /dev/null +++ b/tests/lib/test_checksum.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2008 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <stdlib.h> +#include <time.h> + +#include "checksum.h" +#include "network.h" +#include "prng.h" + +struct thread_master *master; + +struct acc_vals { + int c0; + int c1; +}; + +struct csum_vals { + struct acc_vals a; + int x; + int y; +}; + +static struct csum_vals ospfd_vals, isisd_vals; + +typedef size_t testsz_t; +typedef uint16_t testoff_t; + +/* Fletcher Checksum -- Refer to RFC1008. */ +#define MODX 4102U + +/* The final reduction phase. + * This one should be the original ospfd version + */ +static uint16_t reduce_ospfd(struct csum_vals *vals, testsz_t len, + testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + + x = ((len - off - 1) * c0 - c1) % 255; + + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + /* take care endian issue. */ + return htons((x << 8) + y); +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* slightly different concatenation */ +static uint16_t reduce_ospfd1(struct csum_vals *vals, testsz_t len, + testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + + x = ((len - off - 1) * c0 - c1) % 255; + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + /* take care endian issue. */ + return htons((x << 8) | (y & 0xff)); +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* original isisd version */ +static uint16_t reduce_isisd(struct csum_vals *vals, testsz_t len, + testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + uint32_t mul; + + mul = (len - off) * (c0); + x = mul - c0 - c1; + y = c1 - mul - 1; + + if (y > 0) + y++; + if (x < 0) + x--; + + x %= 255; + y %= 255; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* Is the -1 in y wrong perhaps? */ +static uint16_t reduce_isisd_yfix(struct csum_vals *vals, testsz_t len, + testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + uint32_t mul; + + mul = (len - off) * (c0); + x = mul - c0 - c1; + y = c1 - mul; + + if (y > 0) + y++; + if (x < 0) + x--; + + x %= 255; + y %= 255; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* Move the mods yp */ +static uint16_t reduce_isisd_mod(struct csum_vals *vals, testsz_t len, + testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + uint32_t mul; + + mul = (len - off) * (c0); + x = mul - c1 - c0; + y = c1 - mul - 1; + + x %= 255; + y %= 255; + + if (y > 0) + y++; + if (x < 0) + x--; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* Move the mods up + fix y */ +static uint16_t reduce_isisd_mody(struct csum_vals *vals, testsz_t len, + testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + uint32_t mul; + + mul = (len - off) * (c0); + x = mul - c0 - c1; + y = c1 - mul; + + x %= 255; + y %= 255; + + if (y > 0) + y++; + if (x < 0) + x--; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +struct reductions_t { + const char *name; + uint16_t (*f)(struct csum_vals *, testsz_t, testoff_t); +} reducts[] = { + {.name = "ospfd", .f = reduce_ospfd}, + {.name = "ospfd-1", .f = reduce_ospfd1}, + {.name = "isisd", .f = reduce_isisd}, + {.name = "isisd-yfix", .f = reduce_isisd_yfix}, + {.name = "isisd-mod", .f = reduce_isisd_mod}, + {.name = "isisd-mody", .f = reduce_isisd_mody}, + {NULL, NULL}, +}; + +/* The original ospfd checksum */ +static uint16_t ospfd_checksum(uint8_t *buffer, testsz_t len, testoff_t off) +{ + uint8_t *sp, *ep, *p, *q; + int c0 = 0, c1 = 0; + int x, y; + uint16_t checksum, *csum; + + csum = (uint16_t *)(buffer + off); + *(csum) = 0; + + sp = buffer; + + for (ep = sp + len; sp < ep; sp = q) { + q = sp + MODX; + if (q > ep) + q = ep; + for (p = sp; p < q; p++) { + c0 += *p; + c1 += c0; + } + c0 %= 255; + c1 %= 255; + } + + ospfd_vals.a.c0 = c0; + ospfd_vals.a.c1 = c1; + + // printf ("%s: len %u, off %u, c0 %d, c1 %d\n", + // __func__, len, off, c0, c1); + + x = ((int)(len - off - 1) * (int)c0 - (int)c1) % 255; + + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + ospfd_vals.x = x; + ospfd_vals.y = y; + + buffer[off] = x; + buffer[off + 1] = y; + + /* take care endian issue. */ + checksum = htons((x << 8) | (y & 0xff)); + + return (checksum); +} + +/* the original, broken isisd checksum */ +static uint16_t iso_csum_create(uint8_t *buffer, testsz_t len, testoff_t off) +{ + + uint8_t *p; + int x; + int y; + uint32_t mul; + uint32_t c0; + uint32_t c1; + uint16_t checksum, *csum; + int i, init_len, partial_len; + + checksum = 0; + + csum = (uint16_t *)(buffer + off); + *(csum) = checksum; + + p = buffer; + c0 = 0; + c1 = 0; + init_len = len; + + while (len != 0) { + partial_len = MIN(len, MODX); + + for (i = 0; i < partial_len; i++) { + c0 = c0 + *(p++); + c1 += c0; + } + + c0 = c0 % 255; + c1 = c1 % 255; + + len -= partial_len; + } + + isisd_vals.a.c0 = c0; + isisd_vals.a.c1 = c1; + + mul = (init_len - off) * c0; + + x = mul - c1 - c0; + y = c1 - mul - 1; + + if (y > 0) + y++; + if (x < 0) + x--; + + x %= 255; + y %= 255; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + isisd_vals.x = x; + isisd_vals.y = y; + + checksum = htons((x << 8) | (y & 0xFF)); + + *(csum) = checksum; + + /* return the checksum for user usage */ + return checksum; +} + +static int verify(uint8_t *buffer, testsz_t len) +{ + uint8_t *p; + uint32_t c0; + uint32_t c1; + int i, partial_len; + + p = buffer; + + c0 = 0; + c1 = 0; + + while (len) { + partial_len = MIN(len, 5803U); + + for (i = 0; i < partial_len; i++) { + c0 = c0 + *(p++); + c1 += c0; + } + c0 = c0 % 255; + c1 = c1 % 255; + + len -= partial_len; + } + + if (c0 == 0 && c1 == 0) + return 0; + + return 1; +} + +static int /* return checksum in low-order 16 bits */ + in_cksum_optimized(void *parg, int nbytes) +{ + unsigned short *ptr = parg; + register long sum; /* assumes long == 32 bits */ + register unsigned short answer; /* assumes unsigned short == 16 bits */ + register int count; + /* + * Our algorithm is simple, using a 32-bit accumulator (sum), + * we add sequential 16-bit words to it, and at the end, fold back + * all the carry bits from the top 16 bits into the lower 16 bits. + */ + + sum = 0; + count = nbytes >> 1; /* div by 2 */ + for (ptr--; count; --count) + sum += *++ptr; + + if (nbytes & 1) /* Odd */ + sum += *(uint8_t *)(++ptr); /* one byte only */ + + /* + * Add back carry outs from top 16 bits to low 16 bits. + */ + + sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* ones-complement, then truncate to 16 bits */ + return (answer); +} + + +static int /* return checksum in low-order 16 bits */ + in_cksum_rfc(void *parg, int count) +/* from RFC 1071 */ +{ + unsigned short *addr = parg; + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + register long sum = 0; + + while (count > 1) { + /* This is the inner loop */ + sum += *addr++; + count -= 2; + } + /* Add left-over byte, if any */ + if (count > 0) { + sum += *(uint8_t *)addr; + } + + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + return ~sum; +} + + +int main(int argc, char **argv) +{ +/* 60017 65629 702179 */ +#define MAXDATALEN 60017 +#define BUFSIZE MAXDATALEN + sizeof(uint16_t) + uint8_t buffer[BUFSIZE]; + int exercise = 0; +#define EXERCISESTEP 257 + struct prng *prng = prng_new(0); + + while (1) { + uint16_t ospfd, isisd, lib, in_csum, in_csum_res, in_csum_rfc; + int i; + + exercise += EXERCISESTEP; + exercise %= MAXDATALEN; + + printf("\rexercising length %d\033[K", exercise); + + for (i = 0; i < exercise; i++) + buffer[i] = prng_rand(prng); + + in_csum = in_cksum(buffer, exercise); + in_csum_res = in_cksum_optimized(buffer, exercise); + in_csum_rfc = in_cksum_rfc(buffer, exercise); + if (in_csum_res != in_csum || in_csum != in_csum_rfc) + printf("\nverify: in_chksum failed in_csum:%x, in_csum_res:%x,in_csum_rfc %x, len:%d\n", + in_csum, in_csum_res, in_csum_rfc, exercise); + + struct iovec iov[3]; + uint16_t in_csum_iov; + + iov[0].iov_base = buffer; + iov[0].iov_len = exercise / 2; + iov[1].iov_base = buffer + iov[0].iov_len; + iov[1].iov_len = exercise - iov[0].iov_len; + + in_csum_iov = in_cksumv(iov, 2); + if (in_csum_iov != in_csum) + printf("\nverify: in_cksumv failed, lens: %zu+%zu\n", + iov[0].iov_len, iov[1].iov_len); + + if (exercise >= 6) { + /* force split with byte leftover */ + iov[0].iov_base = buffer; + iov[0].iov_len = (exercise / 2) | 1; + iov[1].iov_base = buffer + iov[0].iov_len; + iov[1].iov_len = 2; + iov[2].iov_base = buffer + iov[0].iov_len + 2; + iov[2].iov_len = exercise - iov[0].iov_len - 2; + + in_csum_iov = in_cksumv(iov, 3); + if (in_csum_iov != in_csum) + printf("\nverify: in_cksumv failed, lens: %zu+%zu+%zu, got %04x, expected %04x\n", + iov[0].iov_len, iov[1].iov_len, + iov[2].iov_len, in_csum_iov, in_csum); + + /* force split without byte leftover */ + iov[0].iov_base = buffer; + iov[0].iov_len = (exercise / 2) & ~1UL; + iov[1].iov_base = buffer + iov[0].iov_len; + iov[1].iov_len = 2; + iov[2].iov_base = buffer + iov[0].iov_len + 2; + iov[2].iov_len = exercise - iov[0].iov_len - 2; + + in_csum_iov = in_cksumv(iov, 3); + if (in_csum_iov != in_csum) + printf("\nverify: in_cksumv failed, lens: %zu+%zu+%zu, got %04x, expected %04x\n", + iov[0].iov_len, iov[1].iov_len, + iov[2].iov_len, in_csum_iov, in_csum); + } + + if (exercise >= FLETCHER_CHECKSUM_VALIDATE) + continue; + + ospfd = ospfd_checksum(buffer, exercise + sizeof(uint16_t), + exercise); + if (verify(buffer, exercise + sizeof(uint16_t))) + printf("\nverify: ospfd failed\n"); + isisd = iso_csum_create(buffer, exercise + sizeof(uint16_t), + exercise); + if (verify(buffer, exercise + sizeof(uint16_t))) + printf("\nverify: isisd failed\n"); + lib = fletcher_checksum(buffer, exercise + sizeof(uint16_t), + exercise); + if (verify(buffer, exercise + sizeof(uint16_t))) + printf("\nverify: lib failed\n"); + + if (ospfd != lib) { + printf("\nMismatch in values at size %d\n" + "ospfd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n" + "isisd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n" + "lib: 0x%04x\n", + exercise, ospfd, ospfd_vals.a.c0, + ospfd_vals.a.c1, ospfd_vals.x, ospfd_vals.y, + isisd, isisd_vals.a.c0, isisd_vals.a.c1, + isisd_vals.x, isisd_vals.y, lib); + + /* Investigate reduction phase discrepencies */ + if (ospfd_vals.a.c0 == isisd_vals.a.c0 + && ospfd_vals.a.c1 == isisd_vals.a.c1) { + printf("\n"); + for (i = 0; reducts[i].name != NULL; i++) { + ospfd = reducts[i].f( + &ospfd_vals, + exercise + sizeof(uint16_t), + exercise); + printf("%20s: x: %02x, y %02x, checksum 0x%04x\n", + reducts[i].name, + ospfd_vals.x & 0xff, + ospfd_vals.y & 0xff, ospfd); + } + } + + printf("\n uint8_t testdata [] = {\n "); + for (i = 0; i < exercise; i++) { + printf("0x%02x,%s", buffer[i], + (i + 1) % 8 ? " " : "\n "); + } + printf("\n}\n"); + exit(1); + } + } +} diff --git a/tests/lib/test_frrlua.c b/tests/lib/test_frrlua.c new file mode 100644 index 0000000..fb6b77c --- /dev/null +++ b/tests/lib/test_frrlua.c @@ -0,0 +1,111 @@ +/* + * frrlua unit tests + * Copyright (C) 2021 Donald Lee + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "string.h" +#include "stdio.h" +#include "lib/frrlua.h" + +static void test_encode_decode(void) +{ + lua_State *L = luaL_newstate(); + + long long a = 123; + long long b = a; + + lua_pushintegerp(L, &a); + lua_decode_integerp(L, -1, &a); + assert(a == b); + assert(lua_gettop(L) == 0); + + time_t time_a = 100; + time_t time_b = time_a; + + lua_pushtimet(L, &time_a); + lua_decode_timet(L, -1, &time_a); + assert(time_a == time_b); + assert(lua_gettop(L) == 0); + + char str_b[] = "Hello", str_a[6]; + + strlcpy(str_a, str_b, sizeof(str_b)); + lua_pushstring_wrapper(L, str_a); + lua_decode_stringp(L, -1, str_a); + assert(strncmp(str_a, str_b, sizeof(str_b)) == 0); + assert(lua_gettop(L) == 0); + + char p_b_str[] = "10.0.0.0/24", p_a_str[12]; + struct prefix p_a; + + strlcpy(p_a_str, p_b_str, sizeof(p_b_str)); + str2prefix(p_a_str, &p_a); + lua_pushprefix(L, &p_a); + lua_decode_prefix(L, -1, &p_a); + prefix2str(&p_a, p_a_str, sizeof(p_b_str)); + assert(strncmp(p_a_str, p_b_str, sizeof(p_b_str)) == 0); + assert(lua_gettop(L) == 0); + + struct interface ifp_a = {}; + struct interface ifp_b = ifp_a; + + lua_pushinterface(L, &ifp_a); + lua_decode_interface(L, -1, &ifp_a); + assert(strncmp(ifp_a.name, ifp_b.name, sizeof(ifp_b.name)) == 0); + assert(ifp_a.ifindex == ifp_b.ifindex); + assert(ifp_a.status == ifp_b.status); + assert(ifp_a.flags == ifp_b.flags); + assert(ifp_a.metric == ifp_b.metric); + assert(ifp_a.speed == ifp_b.speed); + assert(ifp_a.mtu == ifp_b.mtu); + assert(ifp_a.mtu6 == ifp_b.mtu6); + assert(ifp_a.bandwidth == ifp_b.bandwidth); + assert(ifp_a.link_ifindex == ifp_b.link_ifindex); + assert(ifp_a.ll_type == ifp_b.ll_type); + assert(lua_gettop(L) == 0); + + struct in_addr addr_a = {}; + struct in_addr addr_b = addr_a; + + lua_pushinaddr(L, &addr_a); + lua_decode_inaddr(L, -1, &addr_a); + assert(addr_a.s_addr == addr_b.s_addr); + assert(lua_gettop(L) == 0); + + struct in6_addr in6addr_a = {}; + struct in6_addr in6addr_b = in6addr_a; + + lua_pushin6addr(L, &in6addr_a); + lua_decode_in6addr(L, -1, &in6addr_a); + assert(in6addr_cmp(&in6addr_a, &in6addr_b) == 0); + assert(lua_gettop(L) == 0); + + union sockunion su_a, su_b; + + memset(&su_a, 0, sizeof(union sockunion)); + memset(&su_b, 0, sizeof(union sockunion)); + lua_pushsockunion(L, &su_a); + lua_decode_sockunion(L, -1, &su_a); + assert(sockunion_cmp(&su_a, &su_b) == 0); + assert(lua_gettop(L) == 0); +} + +int main(int argc, char **argv) +{ + test_encode_decode(); +} diff --git a/tests/lib/test_frrlua.py b/tests/lib/test_frrlua.py new file mode 100644 index 0000000..2f6ddc1 --- /dev/null +++ b/tests/lib/test_frrlua.py @@ -0,0 +1,14 @@ +import frrtest +import pytest + +if 'S["SCRIPTING_TRUE"]=""\n' not in open("../config.status").readlines(): + class TestFrrlua: + @pytest.mark.skipif(True, reason="Test unsupported") + def test_exit_cleanly(self): + pass +else: + + class TestFrrlua(frrtest.TestMultiOut): + program = "./test_frrlua" + + TestFrrlua.exit_cleanly() diff --git a/tests/lib/test_frrscript.c b/tests/lib/test_frrscript.c new file mode 100644 index 0000000..4c5d8d2 --- /dev/null +++ b/tests/lib/test_frrscript.c @@ -0,0 +1,112 @@ +/* + * frrscript unit tests + * Copyright (C) 2021 Donald Lee + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "lib/frrscript.h" +#include "lib/frrlua.h" + +int main(int argc, char **argv) +{ + frrscript_init("./lib"); + struct frrscript *fs = frrscript_new("script1"); + int result; + + /* Positive testing */ + + long long a = 100, b = 200; + + result = frrscript_load(fs, "foo", NULL); + assert(result == 0); + result = frrscript_call(fs, "foo", ("a", &a), ("b", &b)); + assert(result == 0); + assert(a == 101); + assert(b == 201); + + a = 100, b = 200; + + result = frrscript_load(fs, "bar", NULL); + assert(result == 0); + result = frrscript_call(fs, "bar", ("a", &a), ("b", &b)); + assert(result == 0); + long long *cptr = frrscript_get_result(fs, "bar", "c", lua_tointegerp); + + /* a should not occur in the returned table in script */ + assert(a == 100); + assert(b == 201); + assert(*cptr == 303); + XFREE(MTYPE_SCRIPT_RES, cptr); + + long long n = 5; + + result = frrscript_load(fs, "fact", NULL); + assert(result == 0); + result = frrscript_call(fs, "fact", ("n", &n)); + assert(result == 0); + long long *ansptr = + frrscript_get_result(fs, "fact", "ans", lua_tointegerp); + assert(*ansptr == 120); + + /* check consecutive call + get_result without re-loading */ + n = 4; + result = frrscript_call(fs, "fact", ("n", &n)); + assert(result == 0); + ansptr = frrscript_get_result(fs, "fact", "ans", lua_tointegerp); + assert(*ansptr == 24); + + XFREE(MTYPE_SCRIPT_RES, ansptr); + + /* Negative testing */ + + /* Function does not exist in script file*/ + result = frrscript_load(fs, "does_not_exist", NULL); + assert(result == 1); + + /* Function was not (successfully) loaded */ + result = frrscript_call(fs, "does_not_exist", ("a", &a), ("b", &b)); + assert(result == 1); + + /* Get result from a function that was not loaded */ + long long *llptr = + frrscript_get_result(fs, "does_not_exist", "c", lua_tointegerp); + assert(llptr == NULL); + + /* Function returns void */ + result = frrscript_call(fs, "bad_return1"); + assert(result == 1); + + /* Function returns number */ + result = frrscript_call(fs, "bad_return2"); + assert(result == 1); + + /* Get non-existent result from a function */ + result = frrscript_call(fs, "bad_return3"); + assert(result == 1); + long long *cllptr = + frrscript_get_result(fs, "bad_return3", "c", lua_tointegerp); + assert(cllptr == NULL); + + /* Function throws exception */ + result = frrscript_call(fs, "bad_return4"); + assert(result == 1); + + frrscript_delete(fs); + + return 0; +} diff --git a/tests/lib/test_frrscript.py b/tests/lib/test_frrscript.py new file mode 100644 index 0000000..046d97b --- /dev/null +++ b/tests/lib/test_frrscript.py @@ -0,0 +1,14 @@ +import frrtest +import pytest + +if 'S["SCRIPTING_TRUE"]=""\n' not in open("../config.status").readlines(): + class TestFrrscript: + @pytest.mark.skipif(True, reason="Test unsupported") + def test_exit_cleanly(self): + pass +else: + + class TestFrrscript(frrtest.TestMultiOut): + program = "./test_frrscript" + + TestFrrscript.exit_cleanly() diff --git a/tests/lib/test_graph.c b/tests/lib/test_graph.c new file mode 100644 index 0000000..2801c48 --- /dev/null +++ b/tests/lib/test_graph.c @@ -0,0 +1,77 @@ +/* + * Test graph data structure. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <graph.h> +#include <memory.h> +#include <buffer.h> + +#define NUMNODES 32 + +static void graph_custom_print_cb(struct graph_node *gn, struct buffer *buf) +{ + char nbuf[64]; + char *gname = gn->data; + + for (unsigned int i = 0; i < vector_active(gn->to); i++) { + struct graph_node *adj = vector_slot(gn->to, i); + char *name = adj->data; + + snprintf(nbuf, sizeof(nbuf), " n%s -> n%s;\n", gname, name); + buffer_putstr(buf, nbuf); + } +} + +int main(int argc, char **argv) +{ + struct graph *g = graph_new(); + struct graph_node *gn[NUMNODES]; + char names[NUMNODES][16]; + + /* create vertices */ + for (unsigned int i = 0; i < NUMNODES; i++) { + snprintf(names[i], sizeof(names[i]), "%u", i); + gn[i] = graph_new_node(g, names[i], NULL); + } + + /* create edges */ + for (unsigned int i = 1; i < NUMNODES - 1; i++) { + graph_add_edge(gn[0], gn[i]); + graph_add_edge(gn[i], gn[i + 1]); + } + graph_add_edge(gn[0], gn[NUMNODES - 1]); + graph_add_edge(gn[NUMNODES - 1], gn[1]); + + /* print DOT */ + char *dumped = graph_dump_dot(g, gn[0], graph_custom_print_cb); + + fprintf(stdout, "%s", dumped); + XFREE(MTYPE_TMP, dumped); + + /* remove some edges */ + for (unsigned int i = NUMNODES - 1; i > NUMNODES / 2; --i) + for (unsigned int j = 0; j < NUMNODES; j++) + graph_remove_edge(gn[i], gn[j]); + + /* remove some nodes */ + for (unsigned int i = 0; i < NUMNODES / 2; i++) + graph_delete_node(g, gn[i]); + + graph_delete_graph(g); +} diff --git a/tests/lib/test_graph.py b/tests/lib/test_graph.py new file mode 100644 index 0000000..b26986c --- /dev/null +++ b/tests/lib/test_graph.py @@ -0,0 +1,5 @@ +import frrtest + + +class TestGraph(frrtest.TestRefOut): + program = "./test_graph" diff --git a/tests/lib/test_graph.refout b/tests/lib/test_graph.refout new file mode 100644 index 0000000..955f552 --- /dev/null +++ b/tests/lib/test_graph.refout @@ -0,0 +1,64 @@ +digraph { + n0 -> n1; + n0 -> n2; + n0 -> n3; + n0 -> n4; + n0 -> n5; + n0 -> n6; + n0 -> n7; + n0 -> n8; + n0 -> n9; + n0 -> n10; + n0 -> n11; + n0 -> n12; + n0 -> n13; + n0 -> n14; + n0 -> n15; + n0 -> n16; + n0 -> n17; + n0 -> n18; + n0 -> n19; + n0 -> n20; + n0 -> n21; + n0 -> n22; + n0 -> n23; + n0 -> n24; + n0 -> n25; + n0 -> n26; + n0 -> n27; + n0 -> n28; + n0 -> n29; + n0 -> n30; + n0 -> n31; + n31 -> n1; + n1 -> n2; + n2 -> n3; + n3 -> n4; + n4 -> n5; + n5 -> n6; + n6 -> n7; + n7 -> n8; + n8 -> n9; + n9 -> n10; + n10 -> n11; + n11 -> n12; + n12 -> n13; + n13 -> n14; + n14 -> n15; + n15 -> n16; + n16 -> n17; + n17 -> n18; + n18 -> n19; + n19 -> n20; + n20 -> n21; + n21 -> n22; + n22 -> n23; + n23 -> n24; + n24 -> n25; + n25 -> n26; + n26 -> n27; + n27 -> n28; + n28 -> n29; + n29 -> n30; + n30 -> n31; +} diff --git a/tests/lib/test_grpc.cpp b/tests/lib/test_grpc.cpp new file mode 100644 index 0000000..88372f4 --- /dev/null +++ b/tests/lib/test_grpc.cpp @@ -0,0 +1,979 @@ +/* + * May 16 2021, Christian Hopps <chopps@labn.net> + * + * Copyright (c) 2021, LabN Consulting, L.L.C + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <time.h> +#include <unistd.h> +#include <zebra.h> + +#include "filter.h" +#include "frr_pthread.h" +#include "libfrr.h" +#include "routing_nb.h" +#include "northbound_cli.h" +#include "thread.h" +#include "vrf.h" +#include "vty.h" + +#include "staticd/static_debug.h" +#include "staticd/static_nb.h" +#include "staticd/static_vrf.h" +#include "staticd/static_vty.h" +#include "staticd/static_zebra.h" + +// GRPC C++ includes +#include <string> +#include <sstream> +#include <grpc/grpc.h> +#include <grpcpp/channel.h> +#include <grpcpp/client_context.h> +#include <grpcpp/create_channel.h> +#include <grpcpp/security/credentials.h> +#include "grpc/frr-northbound.grpc.pb.h" + +DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)); +DEFINE_KOOH(frr_fini, (), ()); + +struct vty *vty; + +bool mpls_enabled; +struct thread_master *master; +struct zebra_privs_t static_privs = {0}; +struct frrmod_runtime *grpc_module; +char binpath[2 * MAXPATHLEN + 1]; + +extern const char *json_expect1; +extern const char *json_expect2; +extern const char *json_expect3; +extern const char *json_loadconf1; + +int test_dbg = 1; + +void inline test_debug(const std::string &s) +{ + if (test_dbg) + std::cout << s << std::endl; +} + +// static struct option_chain modules[] = {{ .arg = "grpc:50051" }] +// static struct option_chain **modnext = modules->next; + +static const struct frr_yang_module_info *const staticd_yang_modules[] = { + &frr_interface_info, &frr_filter_info, &frr_routing_info, + &frr_staticd_info, &frr_vrf_info, +}; + +static void grpc_thread_stop(struct thread *thread); + +static void _err_print(const void *cookie, const char *errstr) +{ + std::cout << "Failed to load grpc module:" << errstr << std::endl; +} + +static void static_startup(void) +{ + // struct frrmod_runtime module; + // static struct option_chain *oc; + + cmd_init(1); + + zlog_aux_init("NONE: ", LOG_DEBUG); + zprivs_preinit(&static_privs); + zprivs_init(&static_privs); + + /* Load the server side module -- check libtool path first */ + std::string modpath = std::string(binpath) + std::string("../../../lib/.libs"); + grpc_module = frrmod_load("grpc:50051", modpath.c_str(), 0, 0); + if (!grpc_module) { + modpath = std::string(binpath) + std::string("../../lib"); + grpc_module = frrmod_load("grpc:50051", modpath.c_str(), + _err_print, 0); + } + if (!grpc_module) + exit(1); + + static_debug_init(); + + master = thread_master_create(NULL); + nb_init(master, staticd_yang_modules, array_size(staticd_yang_modules), + false); + + static_zebra_init(); + vty_init(master, true); + static_vrf_init(); + static_vty_init(); + + hook_register(routing_conf_event, + routing_control_plane_protocols_name_validate); + + routing_control_plane_protocols_register_vrf_dependency(); + + // Add a route + vty = vty_new(); + vty->type = vty::VTY_TERM; + vty_config_enter(vty, true, false); + + auto ret = cmd_execute(vty, "ip route 11.0.0.0/8 Null0", NULL, 0); + assert(!ret); + + ret = cmd_execute(vty, "end", NULL, 0); + assert(!ret); + + nb_cli_pending_commit_check(vty); + + frr_pthread_init(); + + // frr_config_fork(); + hook_call(frr_late_init, master); +} + +static void static_shutdown(void) +{ + hook_call(frr_fini); + vty_close(vty); + vrf_terminate(); + vty_terminate(); + cmd_terminate(); + nb_terminate(); + yang_terminate(); + thread_master_free(master); + master = NULL; +} + +using frr::Northbound; +using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::ClientContext; +using grpc::CompletionQueue; +using grpc::Status; + +class NorthboundClient +{ + public: + NorthboundClient(std::shared_ptr<Channel> channel) + : stub_(frr::Northbound::NewStub(channel)) + { + } + + void Commit(uint32_t candidate_id) + { + frr::CommitRequest request; + frr::CommitResponse reply; + ClientContext context; + Status status; + + request.set_candidate_id(candidate_id); + + request.set_phase(frr::CommitRequest::ALL); + status = stub_->Commit(&context, request, &reply); + _throw_if_not_ok(status); +#if 0 + request.set_phase(frr::CommitRequest::VALIDATE); + status = stub_->Commit(&context, request, &reply); + _throw_if_not_ok(status); + + request.set_phase(frr::CommitRequest::PREPARE); + status = stub_->Commit(&context, request, &reply); + _throw_if_not_ok(status); + + request.set_phase(frr::CommitRequest::APPLY); + status = stub_->Commit(&context, request, &reply); + _throw_if_not_ok(status); +#endif + } + + uint32_t CreateCandidate() + { + frr::CreateCandidateRequest request; + frr::CreateCandidateResponse reply; + ClientContext context; + Status status; + + status = stub_->CreateCandidate(&context, request, &reply); + _throw_if_not_ok(status); + return reply.candidate_id(); + } + + void DeleteCandidate(uint32_t candidate_id) + { + frr::DeleteCandidateRequest request; + frr::DeleteCandidateResponse reply; + ClientContext context; + Status status; + + request.set_candidate_id(candidate_id); + status = stub_->DeleteCandidate(&context, request, &reply); + _throw_if_not_ok(status); + } + + void EditCandidate(uint32_t candidate_id, const std::string &path, + const std::string &value) + { + frr::EditCandidateRequest request; + frr::EditCandidateResponse reply; + ClientContext context; + + request.set_candidate_id(candidate_id); + frr::PathValue *pv = request.add_update(); + pv->set_path(path); + pv->set_value(value); + + Status status = stub_->EditCandidate(&context, request, &reply); + _throw_if_not_ok(status); + } + + std::string Get(const std::string &path, + frr::GetRequest::DataType dtype, frr::Encoding enc, + bool with_defaults) + { + frr::GetRequest request; + frr::GetResponse reply; + ClientContext context; + std::ostringstream ss; + + request.set_type(dtype); + request.set_encoding(enc); + request.set_with_defaults(with_defaults); + request.add_path(path); + + auto stream = stub_->Get(&context, request); + while (stream->Read(&reply)) { + ss << reply.data().data() << std::endl; + } + auto status = stream->Finish(); + _throw_if_not_ok(status); + return ss.str(); + } + + std::string GetCapabilities() + { + frr::GetCapabilitiesRequest request; + frr::GetCapabilitiesResponse reply; + ClientContext context; + + Status status = + stub_->GetCapabilities(&context, request, &reply); + _throw_if_not_ok(status); + + std::ostringstream ss; + ss << "Capabilities:" << std::endl + << "\tVersion: " << reply.frr_version() << std::endl + << "\tRollback Support: " << reply.rollback_support() + << std::endl + << "\tSupported Modules:"; + + for (int i = 0; i < reply.supported_modules_size(); i++) { + auto sm = reply.supported_modules(i); + ss << std::endl + << "\t\tName: \"" << sm.name() + << "\" Revision: " << sm.revision() << " Org: \"" + << sm.organization() << "\""; + } + + ss << std::endl << "\tSupported Encodings:"; + + for (int i = 0; i < reply.supported_encodings_size(); i++) { + auto se = reply.supported_encodings(i); + auto desc = + google::protobuf::GetEnumDescriptor<decltype( + se)>(); + ss << std::endl + << "\t\t" << desc->FindValueByNumber(se)->name(); + } + + ss << std::endl; + + return ss.str(); + } + + void LoadToCandidate(uint32_t candidate_id, bool is_replace, + bool is_json, const std::string &data) + { + frr::LoadToCandidateRequest request; + frr::LoadToCandidateResponse reply; + frr::DataTree *dt = new frr::DataTree; + ClientContext context; + + request.set_candidate_id(candidate_id); + request.set_type(is_replace + ? frr::LoadToCandidateRequest::REPLACE + : frr::LoadToCandidateRequest::MERGE); + dt->set_encoding(is_json ? frr::JSON : frr::XML); + dt->set_data(data); + request.set_allocated_config(dt); + + Status status = + stub_->LoadToCandidate(&context, request, &reply); + _throw_if_not_ok(status); + } + + std::string ListTransactions() + { + frr::ListTransactionsRequest request; + frr::ListTransactionsResponse reply; + ClientContext context; + std::ostringstream ss; + + auto stream = stub_->ListTransactions(&context, request); + + while (stream->Read(&reply)) { + ss << "Tx ID: " << reply.id() + << " client: " << reply.client() + << " date: " << reply.date() + << " comment: " << reply.comment() << std::endl; + } + + auto status = stream->Finish(); + _throw_if_not_ok(status); + return ss.str(); + } + + private: + std::unique_ptr<frr::Northbound::Stub> stub_; + + void _throw_if_not_ok(Status &status) + { + if (!status.ok()) + throw std::runtime_error( + std::to_string(status.error_code()) + ": " + + status.error_message()); + } +}; + + +bool stop = false; + +int grpc_client_test_stop(struct frr_pthread *fpt, void **result) +{ + test_debug("client: STOP pthread"); + + assert(fpt->running); + atomic_store_explicit(&fpt->running, false, memory_order_relaxed); + + test_debug("client: joining pthread"); + pthread_join(fpt->thread, result); + + test_debug("client: joined pthread"); + return 0; +} + +int find_first_diff(const std::string &s1, const std::string &s2) +{ + int s1len = s1.length(); + int s2len = s2.length(); + int mlen = std::min(s1len, s2len); + + for (int i = 0; i < mlen; i++) + if (s1[i] != s2[i]) + return i; + return s1len == s2len ? -1 : mlen; +} + +void assert_no_diff(const std::string &s1, const std::string &s2) +{ + int pos = find_first_diff(s1, s2); + if (pos == -1) + return; + std::cout << "not ok" << std::endl; + std::cout << "Same: " << s1.substr(0, pos) << std::endl; + std::cout << "Diff s1: " << s1.substr(pos) << std::endl; + std::cout << "Diff s2: " << s2.substr(pos) << std::endl; + assert(false); +} + +void assert_config_same(NorthboundClient &client, const std::string &compare) +{ + std::string confs = client.Get("/frr-routing:routing", + frr::GetRequest::ALL, frr::JSON, true); + assert_no_diff(confs, compare); + std::cout << "ok" << std::endl; +} + +void grpc_client_run_test(void) +{ + NorthboundClient client(grpc::CreateChannel( + "localhost:50051", grpc::InsecureChannelCredentials())); + + std::string reply = client.GetCapabilities(); + + uint32_t cid; + cid = client.CreateCandidate(); + std::cout << "CreateCandidate -> " << cid << std::endl; + assert(cid == 1); + client.DeleteCandidate(cid); + std::cout << "DeleteCandidate(" << cid << ")" << std::endl; + cid = client.CreateCandidate(); + assert(cid == 2); + std::cout << "CreateCandidate -> " << cid << std::endl; + + /* + * Get initial configuration + */ + std::cout << "Comparing initial config..."; + assert_config_same(client, json_expect1); + + /* + * Add config using EditCandidate + */ + + char xpath_buf[1024]; + strlcpy(xpath_buf, + "/frr-routing:routing/control-plane-protocols/" + "control-plane-protocol[type='frr-staticd:staticd']" + "[name='staticd'][vrf='default']/frr-staticd:staticd/route-list", + sizeof(xpath_buf)); + int slen = strlen(xpath_buf); + for (int i = 0; i < 4; i++) { + snprintf(xpath_buf + slen, sizeof(xpath_buf) - slen, + "[prefix='13.0.%d.0/24']" + "[afi-safi='frr-routing:ipv4-unicast']/" + "path-list[table-id='0'][distance='1']/" + "frr-nexthops/nexthop[nh-type='blackhole']" + "[vrf='default'][gateway=''][interface='(null)']", + i); + client.EditCandidate(cid, xpath_buf, ""); + } + client.Commit(cid); + std::cout << "Comparing EditCandidate config..."; + assert_config_same(client, json_expect2); + + client.DeleteCandidate(cid); + std::cout << "DeleteCandidate(" << cid << ")" << std::endl; + + /* + * Add config using LoadToCandidate + */ + + cid = client.CreateCandidate(); + std::cout << "CreateCandidate -> " << cid << std::endl; + + client.LoadToCandidate(cid, false, true, json_loadconf1); + client.Commit(cid); + + std::cout << "Comparing LoadToCandidate config..."; + assert_config_same(client, json_expect3); + + client.DeleteCandidate(cid); + std::cout << "DeleteCandidate(" << cid << ")" << std::endl; + + std::string ltxreply = client.ListTransactions(); + // std::cout << "client: pthread received: " << ltxreply << std::endl; +} + +void *grpc_client_test_start(void *arg) +{ + struct frr_pthread *fpt = (struct frr_pthread *)arg; + fpt->master->owner = pthread_self(); + frr_pthread_set_name(fpt); + frr_pthread_notify_running(fpt); + + try { + grpc_client_run_test(); + std::cout << "TEST PASSED" << std::endl; + } catch (std::exception &e) { + std::cout << "Exception in test: " << e.what() << std::endl; + } + + // Signal FRR event loop to stop + test_debug("client: pthread: adding event to stop us"); + thread_add_event(master, grpc_thread_stop, NULL, 0, NULL); + + test_debug("client: pthread: DONE (returning)"); + + return NULL; +} + +static void grpc_thread_start(struct thread *thread) +{ + struct frr_pthread_attr client = { + .start = grpc_client_test_start, + .stop = grpc_client_test_stop, + }; + + auto pth = frr_pthread_new(&client, "GRPC Client thread", "grpc"); + frr_pthread_run(pth, NULL); + frr_pthread_wait_running(pth); +} + +static void grpc_thread_stop(struct thread *thread) +{ + std::cout << __func__ << ": frr_pthread_stop_all" << std::endl; + frr_pthread_stop_all(); + std::cout << __func__ << ": static_shutdown" << std::endl; + static_shutdown(); + std::cout << __func__ << ": exit cleanly" << std::endl; + exit(0); +} + +/* + * return abs path to this binary with trailing `/`. Does not parse path + * environment to find in path, which should not matter for unit testing. + */ +static int get_binpath(const char *argv0, char cwd[2 * MAXPATHLEN + 1]) +{ + const char *rch; + if (argv0[0] == '/') { + *cwd = 0; + rch = strrchr(argv0, '/'); + strlcpy(cwd, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1)); + return 0; + } + if (!(rch = strrchr(argv0, '/'))) { + /* Does not handle using PATH, shouldn't matter for test */ + errno = EINVAL; + return -1; + } + if (!getcwd(cwd, MAXPATHLEN)) + return -1; + int len = strlen(cwd); + cwd[len++] = '/'; + strlcpy(cwd + len, argv0, MIN(rch - argv0 + 2, 2 * MAXPATHLEN + 1)); + return 0; +} + +int main(int argc, char **argv) +{ + assert(argc >= 1); + if (get_binpath(argv[0], binpath) < 0) + exit(1); + + static_startup(); + + thread_add_event(master, grpc_thread_start, NULL, 0, NULL); + + /* Event Loop */ + struct thread thread; + while (thread_fetch(master, &thread)) + thread_call(&thread); + return 0; +} + +// clang-format off + +const char *json_expect1 = R"NONCE({ + "frr-routing:routing": { + "control-plane-protocols": { + "control-plane-protocol": [ + { + "type": "frr-staticd:staticd", + "name": "staticd", + "vrf": "default", + "frr-staticd:staticd": { + "route-list": [ + { + "prefix": "11.0.0.0/8", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + } + ] + } + } + ] + } + }, + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "active": false + } + } + ] + } +} + +)NONCE"; + +const char *json_loadconf1 = R"NONCE( +{ + "frr-routing:routing": { + "control-plane-protocols": { + "control-plane-protocol": [ + { + "type": "frr-staticd:staticd", + "name": "staticd", + "vrf": "default", + "frr-staticd:staticd": { + "route-list": [ + { + "prefix": "10.0.0.0/13", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)" + } + ] + } + } + ] + } + ] + } + } + ] + } + }, + "frr-vrf:lib": { + "vrf": [ + { + "name": "default" + } + ] + } +})NONCE"; + +const char *json_expect2 = R"NONCE({ + "frr-routing:routing": { + "control-plane-protocols": { + "control-plane-protocol": [ + { + "type": "frr-staticd:staticd", + "name": "staticd", + "vrf": "default", + "frr-staticd:staticd": { + "route-list": [ + { + "prefix": "11.0.0.0/8", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.1.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.2.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.3.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + } + ] + } + } + ] + } + }, + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "active": false + } + } + ] + } +} + +)NONCE"; + +const char *json_expect3 = R"NONCE({ + "frr-routing:routing": { + "control-plane-protocols": { + "control-plane-protocol": [ + { + "type": "frr-staticd:staticd", + "name": "staticd", + "vrf": "default", + "frr-staticd:staticd": { + "route-list": [ + { + "prefix": "11.0.0.0/8", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.0.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.1.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.2.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "13.0.3.0/24", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + }, + { + "prefix": "10.0.0.0/13", + "afi-safi": "frr-routing:ipv4-unicast", + "path-list": [ + { + "table-id": 0, + "distance": 1, + "tag": 0, + "frr-nexthops": { + "nexthop": [ + { + "nh-type": "blackhole", + "vrf": "default", + "gateway": "", + "interface": "(null)", + "bh-type": "null", + "onlink": false + } + ] + } + } + ] + } + ] + } + } + ] + } + }, + "frr-vrf:lib": { + "vrf": [ + { + "name": "default", + "state": { + "active": false + } + } + ] + } +} + +)NONCE"; diff --git a/tests/lib/test_grpc.py b/tests/lib/test_grpc.py new file mode 100644 index 0000000..7f722de --- /dev/null +++ b/tests/lib/test_grpc.py @@ -0,0 +1,33 @@ +import inspect +import os +import subprocess + +import frrtest +import pytest + + +class TestGRPC(object): + program = "./test_grpc" + + @pytest.mark.skipif( + 'S["GRPC_TRUE"]=""\n' not in open("../config.status").readlines(), + reason="GRPC not enabled", + ) + @pytest.mark.skipif( + not os.path.isdir("/usr/share/yang"), + reason="YANG models aren't installed in /usr/share/yang", + ) + def test_exits_cleanly(self): + basedir = os.path.dirname(inspect.getsourcefile(type(self))) + program = os.path.join(basedir, self.program) + proc = subprocess.Popen( + [frrtest.binpath(program)], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + output, _ = proc.communicate() + self.exitcode = proc.wait() + if self.exitcode != 0: + print("OUTPUT:\n" + output.decode("ascii")) + raise frrtest.TestExitNonzero(self) diff --git a/tests/lib/test_heavy.c b/tests/lib/test_heavy.c new file mode 100644 index 0000000..afc7a08 --- /dev/null +++ b/tests/lib/test_heavy.c @@ -0,0 +1,103 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This programme shows the effects of 'heavy' long-running functions + * on the cooperative threading model. + * + * Run it with a config file containing 'password whatever', telnet to it + * (it defaults to port 4000) and enter the 'clear foo string' command. + * then type whatever and observe that the vty interface is unresponsive + * for quite a period of time, due to the clear_something command + * taking a very long time to complete. + */ +#include <zebra.h> + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include <math.h> + +#include "tests.h" + +enum { ITERS_FIRST = 0, + ITERS_ERR = 100, + ITERS_LATER = 400, + ITERS_PRINT = 10, + ITERS_MAX = 1000, +}; + +static void slow_func(struct vty *vty, const char *str, const int i) +{ + double x = 1; + int j; + + for (j = 0; j < 300; j++) + x += sin(x) * j; + + if ((i % ITERS_LATER) == 0) + printf("%s: %d, temporary error, save this somehow and do it later..\n", + __func__, i); + + if ((i % ITERS_ERR) == 0) + printf("%s: hard error\n", __func__); + + if ((i % ITERS_PRINT) == 0) + printf("%s did %d, x = %g\n", str, i, x); +} + +static void clear_something(struct vty *vty, const char *str) +{ + int i; + + /* this could be like iterating through 150k of route_table + * or worse, iterating through a list of peers, to bgp_stop them with + * each having 150k route tables to process... + */ + for (i = ITERS_FIRST; i < ITERS_MAX; i++) + slow_func(vty, str, i); +} + +DEFUN (clear_foo, + clear_foo_cmd, + "clear foo LINE...", + "clear command\n" + "arbitrary string\n") +{ + char *str; + if (!argc) { + vty_out(vty, "%% string argument required\n"); + return CMD_WARNING; + } + + str = argv_concat(argv, argc, 0); + + clear_something(vty, str); + XFREE(MTYPE_TMP, str); + return CMD_SUCCESS; +} + +static void slow_vty_init(void) +{ + install_element(VIEW_NODE, &clear_foo_cmd); +} + +void test_init(void) +{ + slow_vty_init(); +} diff --git a/tests/lib/test_heavy_thread.c b/tests/lib/test_heavy_thread.c new file mode 100644 index 0000000..466f9a7 --- /dev/null +++ b/tests/lib/test_heavy_thread.c @@ -0,0 +1,127 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This programme shows the effects of 'heavy' long-running functions + * on the cooperative threading model, as demonstrated by heavy.c, and how + * they can be mitigated using a background thread. + * + * Run it with a config file containing 'password whatever', telnet to it + * (it defaults to port 4000) and enter the 'clear foo string' command. + * then type whatever and observe that, unlike heavy.c, the vty interface + * remains responsive. + */ +#include <zebra.h> +#include <math.h> + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "log.h" + +#include "tests.h" + +extern struct thread_master *master; + +enum { ITERS_FIRST = 0, + ITERS_ERR = 100, + ITERS_LATER = 400, + ITERS_PRINT = 10, + ITERS_MAX = 1000, +}; + +struct work_state { + struct vty *vty; + char *str; + int i; +}; + +static void slow_func(struct vty *vty, const char *str, const int i) +{ + double x = 1; + int j; + + for (j = 0; j < 300; j++) + x += sin(x) * j; + + if ((i % ITERS_LATER) == 0) + printf("%s: %d, temporary error, save this somehow and do it later..\n", + __func__, i); + + if ((i % ITERS_ERR) == 0) + printf("%s: hard error\n", __func__); + + if ((i % ITERS_PRINT) == 0) + printf("%s did %d, x = %g\n", str, i, x); +} + +static void clear_something(struct thread *thread) +{ + struct work_state *ws = THREAD_ARG(thread); + + /* this could be like iterating through 150k of route_table + * or worse, iterating through a list of peers, to bgp_stop them with + * each having 150k route tables to process... + */ + while (ws->i < ITERS_MAX) { + slow_func(ws->vty, ws->str, ws->i); + ws->i++; + if (thread_should_yield(thread)) { + thread_add_timer_msec(master, clear_something, ws, 0, + NULL); + return; + } + } + + /* All done! */ + XFREE(MTYPE_TMP, ws->str); + XFREE(MTYPE_TMP, ws); +} + +DEFUN (clear_foo, + clear_foo_cmd, + "clear foo LINE...", + "clear command\n" + "arbitrary string\n") +{ + char *str; + struct work_state *ws; + + if (!argc) { + vty_out(vty, "%% string argument required\n"); + return CMD_WARNING; + } + + str = argv_concat(argv, argc, 0); + + ws = XMALLOC(MTYPE_TMP, sizeof(*ws)); + + ws->str = XSTRDUP(MTYPE_TMP, str); + + ws->vty = vty; + ws->i = ITERS_FIRST; + + thread_add_timer_msec(master, clear_something, ws, 0, NULL); + + return CMD_SUCCESS; +} + +void test_init(void) +{ + install_element(VIEW_NODE, &clear_foo_cmd); +} diff --git a/tests/lib/test_heavy_wq.c b/tests/lib/test_heavy_wq.c new file mode 100644 index 0000000..00aa7b8 --- /dev/null +++ b/tests/lib/test_heavy_wq.c @@ -0,0 +1,158 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This programme shows the effects of 'heavy' long-running functions + * on the cooperative threading model. + * + * Run it with a config file containing 'password whatever', telnet to it + * (it defaults to port 4000) and enter the 'clear foo string' command. + * then type whatever and observe that the vty interface is unresponsive + * for quite a period of time, due to the clear_something command + * taking a very long time to complete. + */ +#include <zebra.h> + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "log.h" +#include "workqueue.h" +#include <math.h> + +#include "tests.h" + +DEFINE_MGROUP(TEST_HEAVYWQ, "heavy-wq test"); +DEFINE_MTYPE_STATIC(TEST_HEAVYWQ, WQ_NODE, "heavy_wq_node"); +DEFINE_MTYPE_STATIC(TEST_HEAVYWQ, WQ_NODE_STR, "heavy_wq_node->str"); + +extern struct thread_master *master; +static struct work_queue *heavy_wq; + +struct heavy_wq_node { + char *str; + int i; +}; + +enum { ITERS_FIRST = 0, + ITERS_ERR = 100, + ITERS_LATER = 400, + ITERS_PRINT = 10, + ITERS_MAX = 1000, +}; + +static void heavy_wq_add(struct vty *vty, const char *str, int i) +{ + struct heavy_wq_node *hn; + + hn = XCALLOC(MTYPE_WQ_NODE, sizeof(struct heavy_wq_node)); + + hn->i = i; + hn->str = XSTRDUP(MTYPE_WQ_NODE_STR, str); + + work_queue_add(heavy_wq, hn); + + return; +} + +static void slow_func_err(struct work_queue *wq, struct work_queue_item *item) +{ + printf("%s: running error function\n", __func__); +} + +static void slow_func_del(struct work_queue *wq, void *data) +{ + struct heavy_wq_node *hn = data; + assert(hn && hn->str); + printf("%s: %s\n", __func__, hn->str); + XFREE(MTYPE_WQ_NODE_STR, hn->str); + XFREE(MTYPE_WQ_NODE, hn); +} + +static wq_item_status slow_func(struct work_queue *wq, void *data) +{ + struct heavy_wq_node *hn = data; + double x = 1; + int j; + + assert(hn && hn->str); + + for (j = 0; j < 300; j++) + x += sin(x) * j; + + if ((hn->i % ITERS_LATER) == 0) + return WQ_RETRY_LATER; + + if ((hn->i % ITERS_ERR) == 0) + return WQ_RETRY_NOW; + + if ((hn->i % ITERS_PRINT) == 0) + printf("%s did %d, x = %g\n", hn->str, hn->i, x); + + return WQ_SUCCESS; +} + +static void clear_something(struct vty *vty, const char *str) +{ + int i; + + /* this could be like iterating through 150k of route_table + * or worse, iterating through a list of peers, to bgp_stop them with + * each having 150k route tables to process... + */ + for (i = ITERS_FIRST; i < ITERS_MAX; i++) + heavy_wq_add(vty, str, i); +} + +DEFUN (clear_foo, + clear_foo_cmd, + "clear foo LINE...", + "clear command\n" + "arbitrary string\n") +{ + char *str; + if (!argc) { + vty_out(vty, "%% string argument required\n"); + return CMD_WARNING; + } + + str = argv_concat(argv, argc, 0); + + clear_something(vty, str); + XFREE(MTYPE_TMP, str); + return CMD_SUCCESS; +} + +static int heavy_wq_init(void) +{ + heavy_wq = work_queue_new(master, "heavy_work_queue"); + + heavy_wq->spec.workfunc = &slow_func; + heavy_wq->spec.errorfunc = &slow_func_err; + heavy_wq->spec.del_item_data = &slow_func_del; + heavy_wq->spec.max_retries = 3; + heavy_wq->spec.hold = 1000; + + return 0; +} + +void test_init(void) +{ + install_element(VIEW_NODE, &clear_foo_cmd); + heavy_wq_init(); +} diff --git a/tests/lib/test_idalloc.c b/tests/lib/test_idalloc.c new file mode 100644 index 0000000..ce1582b --- /dev/null +++ b/tests/lib/test_idalloc.c @@ -0,0 +1,197 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "id_alloc.h" + +#include <inttypes.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#define IDS_PER_PAGE (1<<(IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS)) +char allocated_markers[IDS_PER_PAGE*3]; + +int main(int argc, char **argv) +{ + int i, val; + uint32_t pg; + struct id_alloc *a; + + /* 1. Rattle test, shake it a little and make sure it doesn't make any + * noise :) + */ + a = idalloc_new("Rattle test"); + for (i = 0; i < 1000000; i++) + assert(idalloc_allocate(a) != 0); + + idalloc_destroy(a); + + /* 2. Reserve a few low IDs, make sure they are skipped by normal + * allocation. + */ + a = idalloc_new("Low Reservations"); + assert(idalloc_reserve(a, 1) == 1); + assert(idalloc_reserve(a, 3) == 3); + assert(idalloc_reserve(a, 5) == 5); + for (i = 0; i < 100; i++) { + val = idalloc_allocate(a); + assert(val != 1 && val != 3 && val != 5); + } + idalloc_destroy(a); + + /* 3. Single page testing. Check that IDs are kept unique, and all IDs + * in the existing page are allocated before a new page is added. + */ + memset(allocated_markers, 0, sizeof(allocated_markers)); + allocated_markers[IDALLOC_INVALID] = 1; + + a = idalloc_new("Single Page"); + + /* reserve the rest of the first page */ + for (i = 0; i < IDS_PER_PAGE - 1; i++) { + val = idalloc_allocate(a); + assert(val < IDS_PER_PAGE); + assert(allocated_markers[val] == 0); + assert(a->capacity == IDS_PER_PAGE); + allocated_markers[val] = 1; + } + /* Check that the count is right */ + assert(a->allocated == IDS_PER_PAGE); + + /* Free some IDs out of the middle. */ + idalloc_free(a, 300); + allocated_markers[300] = 0; + idalloc_free(a, 400); + allocated_markers[400] = 0; + idalloc_free(a, 500); + allocated_markers[500] = 0; + + assert(a->allocated == IDS_PER_PAGE-3); + + /* Allocate the three IDs back and make sure they are pulled from the + * set just freed + */ + for (i = 0; i < 3; i++) { + val = idalloc_allocate(a); + assert(val < IDS_PER_PAGE); + assert(allocated_markers[val] == 0); + assert(a->capacity == IDS_PER_PAGE); + allocated_markers[val] = 1; + } + idalloc_destroy(a); + + /* 4. Multi-page testing. */ + memset(allocated_markers, 0, sizeof(allocated_markers)); + allocated_markers[IDALLOC_INVALID] = 1; + + a = idalloc_new("Multi-page"); + + /* reserve the rest of the first page and all of the second and third */ + for (i = 0; i < 3 * IDS_PER_PAGE - 1; i++) { + val = idalloc_allocate(a); + assert(val < 3*IDS_PER_PAGE); + assert(allocated_markers[val] == 0); + allocated_markers[val] = 1; + } + assert(a->capacity == 3*IDS_PER_PAGE); + assert(a->allocated == 3*IDS_PER_PAGE); + + /* Free two IDs from each page. */ + for (i = 0; i < 3; i++) { + idalloc_free(a, 7 + i*IDS_PER_PAGE); + allocated_markers[7 + i*IDS_PER_PAGE] = 0; + + idalloc_free(a, 4 + i*IDS_PER_PAGE); + allocated_markers[4 + i*IDS_PER_PAGE] = 0; + } + + assert(a->allocated == 3*IDS_PER_PAGE - 6); + + /* Allocate the six IDs back and make sure they are pulled from the set + * just freed. + */ + for (i = 0; i < 6; i++) { + val = idalloc_allocate(a); + assert(val < 3*IDS_PER_PAGE); + assert(allocated_markers[val] == 0); + assert(a->capacity == 3*IDS_PER_PAGE); + allocated_markers[val] = 1; + } + + assert(a->capacity == 3*IDS_PER_PAGE); + assert(a->allocated == 3*IDS_PER_PAGE); + + /* Walk each allocated ID. Free it, then re-allocate it back. */ + for (i = 1; i < 3 * IDS_PER_PAGE - 1; i++) { + idalloc_free(a, i); + val = idalloc_allocate(a); + assert(val == i); + assert(a->capacity == 3*IDS_PER_PAGE); + assert(a->allocated == 3*IDS_PER_PAGE); + } + idalloc_destroy(a); + + /* 5. Weird Reservations + * idalloc_reserve exists primarily to black out low numbered IDs that + * are reserved for special cases. However, we will test it for more + * complex use cases to avoid unpleasant surprises. + */ + + memset(allocated_markers, 0, sizeof(allocated_markers)); + allocated_markers[IDALLOC_INVALID] = 1; + + a = idalloc_new("Weird Reservations"); + + /* Start with 3 pages fully allocated. */ + for (i = 0; i < 3 * IDS_PER_PAGE - 1; i++) { + val = idalloc_allocate(a); + assert(val < 3*IDS_PER_PAGE); + assert(allocated_markers[val] == 0); + allocated_markers[val] = 1; + } + assert(a->capacity == 3*IDS_PER_PAGE); + assert(a->allocated == 3*IDS_PER_PAGE); + + /* Free a bit out of each of the three pages. Then reserve one of the + * three freed IDs. Finally, allocate the other two freed IDs. Do this + * each of three ways. (Reserve out of the first, seconds then third + * page.) + * The intent here is to exercise the rare cases on reserve_bit's + * linked-list removal in the case that it is not removing the first + * page with a free bit in its list of pages with free bits. + */ + + for (pg = 0; pg < 3; pg++) { + /* free a bit out of each of the three pages */ + for (i = 0; i < 3; i++) { + idalloc_free(a, i*IDS_PER_PAGE + 17); + allocated_markers[i*IDS_PER_PAGE + 17] = 0; + } + + assert(a->capacity == 3*IDS_PER_PAGE); + assert(a->allocated == 3*IDS_PER_PAGE-3); + + /* Reserve one of the freed IDs */ + assert(idalloc_reserve(a, pg*IDS_PER_PAGE + 17) == + pg*IDS_PER_PAGE + 17); + allocated_markers[pg*IDS_PER_PAGE + 17] = 1; + + assert(a->capacity == 3*IDS_PER_PAGE); + assert(a->allocated == 3*IDS_PER_PAGE-2); + + /* Allocate the other two back */ + for (i = 0; i < 2; i++) { + val = idalloc_allocate(a); + assert(val < 3*IDS_PER_PAGE); + assert(allocated_markers[val] == 0); + allocated_markers[val] = 1; + } + assert(a->capacity == 3*IDS_PER_PAGE); + assert(a->allocated == 3*IDS_PER_PAGE); + } + idalloc_destroy(a); + + puts("ID Allocator test successful.\n"); + return 0; +} diff --git a/tests/lib/test_idalloc.py b/tests/lib/test_idalloc.py new file mode 100644 index 0000000..e2186dc --- /dev/null +++ b/tests/lib/test_idalloc.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestIDAlloc(frrtest.TestMultiOut): + program = "./test_idalloc" + + +TestIDAlloc.onesimple("ID Allocator test successful.") diff --git a/tests/lib/test_memory.c b/tests/lib/test_memory.c new file mode 100644 index 0000000..9f04304 --- /dev/null +++ b/tests/lib/test_memory.c @@ -0,0 +1,120 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <memory.h> + +DEFINE_MGROUP(TEST_MEMORY, "memory test"); +DEFINE_MTYPE_STATIC(TEST_MEMORY, TEST, "generic test mtype"); + +/* Memory torture tests + * + * Tests below are generic but comments are focused on interaction with + * Paul's proposed memory 'quick' cache, which may never be included in + * CVS + */ + +struct thread_master *master; + +#if 0 /* set to 1 to use system alloc directly */ +#undef XMALLOC +#undef XCALLOC +#undef XREALLOC +#undef XFREE +#define XMALLOC(T,S) malloc((S)) +#define XCALLOC(T,S) calloc(1, (S)) +#define XREALLOC(T,P,S) realloc((P),(S)) +#define XFREE(T,P) free((P)) +#endif + +#define TIMES 10 + +int main(int argc, char **argv) +{ + void *a[10]; + int i; + + printf("malloc x, malloc x, free, malloc x, free free\n\n"); + /* simple case, test cache */ + for (i = 0; i < TIMES; i++) { + a[0] = XMALLOC(MTYPE_TEST, 1024); + memset(a[0], 1, 1024); + a[1] = XMALLOC(MTYPE_TEST, 1024); + memset(a[1], 1, 1024); + XFREE(MTYPE_TEST, a[0]); /* should go to cache */ + a[0] = XMALLOC(MTYPE_TEST, + 1024); /* should be satisfied from cache */ + XFREE(MTYPE_TEST, a[0]); + XFREE(MTYPE_TEST, a[1]); + } + + printf("malloc x, malloc y, free x, malloc y, free free\n\n"); + /* cache should go invalid, valid, invalid, etc.. */ + for (i = 0; i < TIMES; i++) { + a[0] = XMALLOC(MTYPE_TEST, 512); + memset(a[0], 1, 512); + a[1] = XMALLOC(MTYPE_TEST, 1024); /* invalidate cache */ + memset(a[1], 1, 1024); + XFREE(MTYPE_TEST, a[0]); + a[0] = XMALLOC(MTYPE_TEST, 1024); + XFREE(MTYPE_TEST, a[0]); + XFREE(MTYPE_TEST, a[1]); + /* cache should become valid again on next request */ + } + + printf("calloc\n\n"); + /* test calloc */ + for (i = 0; i < TIMES; i++) { + a[0] = XCALLOC(MTYPE_TEST, 1024); + memset(a[0], 1, 1024); + a[1] = XCALLOC(MTYPE_TEST, 512); /* invalidate cache */ + memset(a[1], 1, 512); + XFREE(MTYPE_TEST, a[1]); + XFREE(MTYPE_TEST, a[0]); + /* alloc == 0, cache can become valid again on next request */ + } + + printf("calloc and realloc\n\n"); + /* check calloc + realloc */ + for (i = 0; i < TIMES; i++) { + printf("calloc a0 1024\n"); + a[0] = XCALLOC(MTYPE_TEST, 1024); + memset(a[0], 1, 1024 / 2); + + printf("calloc 1 1024\n"); + a[1] = XCALLOC(MTYPE_TEST, 1024); + memset(a[1], 1, 1024 / 2); + + printf("realloc 0 1024\n"); + a[3] = XREALLOC(MTYPE_TEST, a[0], 2048); /* invalidate cache */ + if (a[3] != NULL) + a[0] = a[3]; + memset(a[0], 1, 1024); + + printf("calloc 2 512\n"); + a[2] = XCALLOC(MTYPE_TEST, 512); + memset(a[2], 1, 512); + + printf("free 1 0 2\n"); + XFREE(MTYPE_TEST, a[1]); + XFREE(MTYPE_TEST, a[0]); + XFREE(MTYPE_TEST, a[2]); + /* alloc == 0, cache valid next request */ + } + return 0; +} diff --git a/tests/lib/test_nexthop.c b/tests/lib/test_nexthop.c new file mode 100644 index 0000000..7cf687d --- /dev/null +++ b/tests/lib/test_nexthop.c @@ -0,0 +1,201 @@ +/* + * Nexthop module test. + * + * Copyright (C) 2021 by Volta Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <nexthop.h> + +static bool verbose; + +static void test_run_first(void) +{ + int ret, i; + struct nexthop *nh1, *nh2; + struct in_addr addr; + struct in6_addr addr6; + mpls_label_t labels[MPLS_MAX_LABELS]; + + /* Test comparison apis */ + + /* ifindex comparisons */ + nh1 = nexthop_from_ifindex(11, 0); + nh2 = nexthop_from_ifindex(12, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret < 0); + + nexthop_free(nh1); + nh1 = nexthop_from_ifindex(12, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret == 0); + + nexthop_free(nh1); + nexthop_free(nh2); + + /* ipv4, vrf */ + addr.s_addr = 0x04030201; + nh1 = nexthop_from_ipv4(&addr, NULL, 0); + nh2 = nexthop_from_ipv4(&addr, NULL, 111); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + nexthop_free(nh2); + + addr.s_addr = 0x04030202; + nh2 = nexthop_from_ipv4(&addr, NULL, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + nexthop_free(nh2); + + addr.s_addr = 0x04030201; + nh2 = nexthop_from_ipv4(&addr, NULL, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret == 0); + + /* Weight */ + nh2->weight = 20; + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + nexthop_free(nh1); + nexthop_free(nh2); + + /* ipv6 */ + memset(addr6.s6_addr, 0, sizeof(addr6.s6_addr)); + nh1 = nexthop_from_ipv6(&addr6, 0); + nh2 = nexthop_from_ipv6(&addr6, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret == 0); + + nexthop_free(nh2); + + nh2 = nexthop_from_ipv6(&addr6, 1); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + nexthop_free(nh2); + + addr6.s6_addr[14] = 1; + addr6.s6_addr[15] = 1; + nh2 = nexthop_from_ipv6(&addr6, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + nexthop_free(nh1); + nexthop_free(nh2); + + /* Blackhole */ + nh1 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0); + nh2 = nexthop_from_blackhole(BLACKHOLE_REJECT, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret == 0); + + nexthop_free(nh2); + + nh2 = nexthop_from_blackhole(BLACKHOLE_NULL, 0); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + /* Labels */ + addr.s_addr = 0x04030201; + nh1 = nexthop_from_ipv4(&addr, NULL, 0); + nh2 = nexthop_from_ipv4(&addr, NULL, 0); + + memset(labels, 0, sizeof(labels)); + labels[0] = 111; + labels[1] = 222; + + nexthop_add_labels(nh1, ZEBRA_LSP_STATIC, 2, labels); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + nexthop_add_labels(nh2, ZEBRA_LSP_STATIC, 2, labels); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret == 0); + + nexthop_free(nh2); + + /* LSP type isn't included */ + nh2 = nexthop_from_ipv4(&addr, NULL, 0); + nexthop_add_labels(nh2, ZEBRA_LSP_LDP, 2, labels); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret == 0); + + nexthop_free(nh2); + + labels[2] = 333; + nh2 = nexthop_from_ipv4(&addr, NULL, 0); + nexthop_add_labels(nh2, ZEBRA_LSP_LDP, 3, labels); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + nexthop_free(nh1); + nexthop_free(nh2); + + nh1 = nexthop_from_ipv4(&addr, NULL, 0); + nh2 = nexthop_from_ipv4(&addr, NULL, 0); + + for (i = 0; i < MPLS_MAX_LABELS; i++) + labels[i] = 111 * (i + 1); + + nexthop_add_labels(nh1, ZEBRA_LSP_LDP, MPLS_MAX_LABELS, labels); + nexthop_add_labels(nh2, ZEBRA_LSP_LDP, MPLS_MAX_LABELS, labels); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret == 0); + + nexthop_free(nh2); + + /* Test very last label in stack */ + labels[15] = 999; + nh2 = nexthop_from_ipv4(&addr, NULL, 0); + nexthop_add_labels(nh2, ZEBRA_LSP_LDP, MPLS_MAX_LABELS, labels); + + ret = nexthop_cmp_basic(nh1, nh2); + assert(ret != 0); + + /* End */ + nexthop_free(nh1); + nexthop_free(nh2); +} + +int main(int argc, char **argv) +{ + if (argc >= 2 && !strcmp("-v", argv[1])) + verbose = true; + test_run_first(); + printf("Simple test passed.\n"); +} diff --git a/tests/lib/test_nexthop.py b/tests/lib/test_nexthop.py new file mode 100644 index 0000000..81bfa43 --- /dev/null +++ b/tests/lib/test_nexthop.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestNexthopIter(frrtest.TestMultiOut): + program = "./test_nexthop" + + +TestNexthopIter.onesimple("Simple test passed.") diff --git a/tests/lib/test_nexthop_iter.c b/tests/lib/test_nexthop_iter.c new file mode 100644 index 0000000..f21f3bb --- /dev/null +++ b/tests/lib/test_nexthop_iter.c @@ -0,0 +1,311 @@ +/* + * Recursive Nexthop Iterator test. + * This tests the ALL_NEXTHOPS macro. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "zebra/rib.h" +#include "prng.h" + +struct thread_master *master; +static int verbose; + +static void str_append(char **buf, const char *repr) +{ + if (*buf) { + *buf = realloc(*buf, strlen(*buf) + strlen(repr) + 1); + assert(*buf); + strncpy((*buf) + strlen(*buf), repr, strlen(repr) + 1); + } else { + *buf = strdup(repr); + assert(*buf); + } +} + +static void str_appendf(char **buf, const char *format, ...) +{ + va_list ap; + int rv; + char *pbuf; + + va_start(ap, format); + rv = vasprintf(&pbuf, format, ap); + va_end(ap); + assert(rv >= 0); + + str_append(buf, pbuf); + free(pbuf); +} + +/* This structure contains a nexthop chain + * and its expected representation */ +struct nexthop_chain { + /* Head of the chain */ + struct nexthop_group head; + /* Last nexthop in top chain */ + struct nexthop *current_top; + /* Last nexthop in current recursive chain */ + struct nexthop *current_recursive; + /* Expected string representation. */ + char *repr; +}; + +static struct nexthop_chain *nexthop_chain_new(void) +{ + struct nexthop_chain *rv; + + rv = calloc(sizeof(*rv), 1); + assert(rv); + return rv; +} + +static void nexthop_chain_add_top(struct nexthop_chain *nc) +{ + struct nexthop *nh; + + nh = calloc(sizeof(*nh), 1); + assert(nh); + + if (nc->head.nexthop) { + nc->current_top->next = nh; + nh->prev = nc->current_top; + nc->current_top = nh; + } else { + nc->head.nexthop = nc->current_top = nh; + } + nc->current_recursive = NULL; + str_appendf(&nc->repr, "%p\n", nh); +} + +static void add_string_representation(char **repr, struct nexthop *nh) +{ + struct nexthop *parent; + + /* add indentations first */ + parent = nh->rparent; + while (parent) { + str_appendf(repr, " "); + parent = parent->rparent; + } + str_appendf(repr, "%p\n", nh); +} + +static void start_recursive_chain(struct nexthop_chain *nc, struct nexthop *nh) +{ + SET_FLAG(nc->current_top->flags, NEXTHOP_FLAG_RECURSIVE); + nc->current_top->resolved = nh; + nh->rparent = nc->current_top; + nc->current_recursive = nh; +} +static void nexthop_chain_add_recursive(struct nexthop_chain *nc) +{ + struct nexthop *nh; + + nh = calloc(sizeof(*nh), 1); + assert(nh); + + assert(nc->current_top); + if (nc->current_recursive) { + nc->current_recursive->next = nh; + nh->prev = nc->current_recursive; + nh->rparent = nc->current_recursive->rparent; + nc->current_recursive = nh; + } else + start_recursive_chain(nc, nh); + + add_string_representation(&nc->repr, nh); +} + +static void nexthop_chain_add_recursive_level(struct nexthop_chain *nc) +{ + struct nexthop *nh; + + nh = calloc(sizeof(*nh), 1); + assert(nh); + + assert(nc->current_top); + if (nc->current_recursive) { + SET_FLAG(nc->current_recursive->flags, NEXTHOP_FLAG_RECURSIVE); + nc->current_recursive->resolved = nh; + nh->rparent = nc->current_recursive; + nc->current_recursive = nh; + } else + start_recursive_chain(nc, nh); + + add_string_representation(&nc->repr, nh); +} + +static void nexthop_clear_recursive(struct nexthop *tcur) +{ + if (!tcur) + return; + if (CHECK_FLAG(tcur->flags, NEXTHOP_FLAG_RECURSIVE)) + nexthop_clear_recursive(tcur->resolved); + if (tcur->next) + nexthop_clear_recursive(tcur->next); + free(tcur); +} +static void nexthop_chain_clear(struct nexthop_chain *nc) +{ + nexthop_clear_recursive(nc->head.nexthop); + nc->head.nexthop = nc->current_top = nc->current_recursive = NULL; + free(nc->repr); + nc->repr = NULL; +} + +static void nexthop_chain_free(struct nexthop_chain *nc) +{ + if (!nc) + return; + nexthop_chain_clear(nc); + free(nc); +} + +/* This function builds a string representation of + * the nexthop chain using the ALL_NEXTHOPS macro. + * It verifies that the ALL_NEXTHOPS macro iterated + * correctly over the nexthop chain by comparing the + * generated representation with the expected representation. + */ +static void nexthop_chain_verify_iter(struct nexthop_chain *nc) +{ + struct nexthop *nh; + char *repr = NULL; + + for (ALL_NEXTHOPS(nc->head, nh)) + add_string_representation(&repr, nh); + + if (repr && verbose) + printf("===\n%s", repr); + assert((!repr && !nc->repr) + || (repr && nc->repr && !strcmp(repr, nc->repr))); + free(repr); +} + +/* This test run builds a simple nexthop chain + * with some recursive nexthops and verifies that + * the iterator works correctly in each stage along + * the way. + */ +static void test_run_first(void) +{ + struct nexthop_chain *nc; + + nc = nexthop_chain_new(); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive_level(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_recursive(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_add_top(nc); + nexthop_chain_verify_iter(nc); + + nexthop_chain_free(nc); +} + +/* This test run builds numerous random + * nexthop chain configurations and verifies + * that the iterator correctly progresses + * through each. */ +static void test_run_prng(void) +{ + struct nexthop_chain *nc; + struct prng *prng; + int i; + + nc = nexthop_chain_new(); + prng = prng_new(0); + + for (i = 0; i < 1000000; i++) { + switch (prng_rand(prng) % 10) { + case 0: + nexthop_chain_clear(nc); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + nexthop_chain_add_top(nc); + break; + case 6: + case 7: + case 8: + if (nc->current_top) + nexthop_chain_add_recursive(nc); + break; + case 9: + if (nc->current_top) + nexthop_chain_add_recursive_level(nc); + break; + } + nexthop_chain_verify_iter(nc); + } + nexthop_chain_free(nc); + prng_free(prng); +} + +int main(int argc, char **argv) +{ + if (argc >= 2 && !strcmp("-v", argv[1])) + verbose = 1; + test_run_first(); + printf("Simple test passed.\n"); + test_run_prng(); + printf("PRNG test passed.\n"); +} diff --git a/tests/lib/test_nexthop_iter.py b/tests/lib/test_nexthop_iter.py new file mode 100644 index 0000000..0c39dce --- /dev/null +++ b/tests/lib/test_nexthop_iter.py @@ -0,0 +1,9 @@ +import frrtest + + +class TestNexthopIter(frrtest.TestMultiOut): + program = "./test_nexthop_iter" + + +TestNexthopIter.onesimple("Simple test passed.") +TestNexthopIter.onesimple("PRNG test passed.") diff --git a/tests/lib/test_ntop.c b/tests/lib/test_ntop.c new file mode 100644 index 0000000..0a55ecd --- /dev/null +++ b/tests/lib/test_ntop.c @@ -0,0 +1,86 @@ +/* + * frr_inet_ntop() unit test + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> + +#include "tests/helpers/c/prng.h" + +/* NB: libfrr is NOT linked for this unit test! */ + +#define INET_NTOP_NO_OVERRIDE +#include "lib/ntop.c" + +int main(int argc, char **argv) +{ + size_t i, j, k, l; + struct in_addr i4; + struct in6_addr i6, i6check; + char buf1[64], buf2[64]; + const char *rv; + struct prng *prng; + + prng = prng_new(0); + /* IPv4 */ + for (i = 0; i < 1000; i++) { + i4.s_addr = prng_rand(prng); + assert(frr_inet_ntop(AF_INET, &i4, buf1, sizeof(buf1))); + assert(inet_ntop(AF_INET, &i4, buf2, sizeof(buf2))); + assert(!strcmp(buf1, buf2)); + } + + /* check size limit */ + for (i = 0; i < sizeof(buf1); i++) { + memset(buf2, 0xcc, sizeof(buf2)); + rv = frr_inet_ntop(AF_INET, &i4, buf2, i); + if (i < strlen(buf1) + 1) + assert(!rv); + else + assert(rv && !strcmp(buf1, buf2)); + } + + /* IPv6 */ + for (i = 0; i < 10000; i++) { + uint16_t *i6w = (uint16_t *)&i6; + for (j = 0; j < 8; j++) + i6w[j] = prng_rand(prng); + + /* clear some words */ + l = prng_rand(prng) & 7; + for (j = 0; j < l; j++) { + uint32_t num = __builtin_ctz(prng_rand(prng)); + uint32_t where = prng_rand(prng) & 7; + + for (k = where; k < where + num && k < 8; k++) + i6w[k] = 0; + } + + assert(frr_inet_ntop(AF_INET6, &i6, buf1, sizeof(buf1))); + assert(inet_ntop(AF_INET6, &i6, buf2, sizeof(buf2))); + if (strcmp(buf1, buf2)) + printf("%-40s (FRR) != (SYS) %-40s\n", buf1, buf2); + + assert(inet_pton(AF_INET6, buf1, &i6check)); + assert(!memcmp(&i6, &i6check, sizeof(i6))); + } + return 0; +} diff --git a/tests/lib/test_ntop.py b/tests/lib/test_ntop.py new file mode 100644 index 0000000..69c4353 --- /dev/null +++ b/tests/lib/test_ntop.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestNtop(frrtest.TestMultiOut): + program = "./test_ntop" + + +TestNtop.exit_cleanly() diff --git a/tests/lib/test_plist.c b/tests/lib/test_plist.c new file mode 100644 index 0000000..ee7a9eb --- /dev/null +++ b/tests/lib/test_plist.c @@ -0,0 +1,48 @@ +/* + * Simple prefix list querying tool + * + * Copyright (C) 2021 by David Lamparter, + * for Open Source Routing / NetDEF, Inc. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "lib/plist.h" +#include "lib/filter.h" +#include "tests/lib/cli/common_cli.h" + +static const struct frr_yang_module_info *const my_yang_modules[] = { + &frr_filter_info, + NULL, +}; + +__attribute__((_CONSTRUCTOR(2000))) +static void test_yang_modules_set(void) +{ + test_yang_modules = my_yang_modules; +} + +void test_init(int argc, char **argv) +{ + prefix_list_init(); + filter_cli_init(); + + /* nothing else to do here, giving stand-alone access to the prefix + * list code's "debug prefix-list ..." command is the only purpose of + * this "test". + */ +} diff --git a/tests/lib/test_prefix2str.c b/tests/lib/test_prefix2str.c new file mode 100644 index 0000000..cbfc20a --- /dev/null +++ b/tests/lib/test_prefix2str.c @@ -0,0 +1,80 @@ +/* + * prefix2str() unit test + * Copyright (C) 2019 David Lamparter + * Portions: + * Copyright (C) 2019 Cumulus Networks, Inc + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> + +#include "lib/prefix.h" + +#include "tests/helpers/c/prng.h" + +int main(int argc, char **argv) +{ + size_t i, j, k, l; + struct in6_addr i6; + char buf1[64], buf2[64], ntopbuf[64]; + struct prng *prng; + struct prefix p = {}; + + prng = prng_new(0); + /* IPv4 */ + p.family = AF_INET; + for (i = 0; i < 1000; i++) { + p.u.prefix = prng_rand(prng); + p.prefixlen = prng_rand(prng) >> 26; + snprintf(buf1, sizeof(buf1), "%s/%d", + inet_ntop(AF_INET, &p.u.prefix4, ntopbuf, + sizeof(ntopbuf)), + p.prefixlen); + prefix2str(&p, buf2, sizeof(buf2)); + assert(!strcmp(buf1, buf2)); + fprintf(stdout, "%s\n", buf1); + } + + /* IPv6 */ + p.family = AF_INET6; + for (i = 0; i < 10000; i++) { + uint16_t *i6w = (uint16_t *)&i6; + for (j = 0; j < 8; j++) + i6w[j] = prng_rand(prng); + + /* clear some words */ + l = prng_rand(prng) & 7; + for (j = 0; j < l; j++) { + uint32_t num = __builtin_ctz(prng_rand(prng)); + uint32_t where = prng_rand(prng) & 7; + + for (k = where; k < where + num && k < 8; k++) + i6w[k] = 0; + } + + p.prefixlen = prng_rand(prng) >> 24; + memcpy(&p.u.prefix, &i6, sizeof(i6)); + snprintf(buf1, sizeof(buf1), "%s/%d", + inet_ntop(AF_INET6, &p.u.prefix6, ntopbuf, + sizeof(ntopbuf)), + p.prefixlen); + prefix2str(&p, buf2, sizeof(buf2)); + assert(!strcmp(buf1, buf2)); + fprintf(stdout, "%s\n", buf1); + } + + return 0; +} diff --git a/tests/lib/test_prefix2str.py b/tests/lib/test_prefix2str.py new file mode 100644 index 0000000..fd883ed --- /dev/null +++ b/tests/lib/test_prefix2str.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestPrefix2str(frrtest.TestMultiOut): + program = "./test_prefix2str" + + +TestPrefix2str.exit_cleanly() diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c new file mode 100644 index 0000000..59d08ae --- /dev/null +++ b/tests/lib/test_printfrr.c @@ -0,0 +1,410 @@ +/* + * printfrr() unit test + * Copyright (C) 2019 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "zebra.h" + +#include <math.h> + +#include "lib/printfrr.h" +#include "lib/memory.h" +#include "lib/prefix.h" +#include "lib/nexthop.h" + +static int errors; + +static void printcmp(const char *fmt, ...) PRINTFRR(1, 2); +static void printcmp(const char *fmt, ...) +{ + va_list ap; + char buf[256], bufrr[256], *p; + int cmp; + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + va_start(ap, fmt); + vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + va_end(ap); + + cmp = strcmp(buf, bufrr); + + /* OS dependent "+nan" vs. "nan" */ + if (cmp && (p = strstr(bufrr, "+nan"))) { + p[0] = ' '; + if (!strcmp(buf, bufrr)) + cmp = 0; + p[0] = '+'; + } + printf("fmt: \"%s\"\nsys: \"%s\"\nfrr: \"%s\"\n%s\n\n", + fmt, buf, bufrr, cmp ? "ERROR" : "ok"); + + if (cmp) + errors++; +} + +static int printchk(const char *ref, const char *fmt, ...) PRINTFRR(2, 3); +static int printchk(const char *ref, const char *fmt, ...) +{ + va_list ap; + char bufrr[256]; + bool truncfail = false; + size_t i; + size_t expectlen; + + memset(bufrr, 0xcc, sizeof(bufrr)); + + va_start(ap, fmt); + expectlen = vsnprintfrr(NULL, 0, fmt, ap); + va_end(ap); + + va_start(ap, fmt); + vsnprintfrr(bufrr, 7, fmt, ap); + va_end(ap); + + if (strnlen(bufrr, 7) == 7) + truncfail = true; + if (strnlen(bufrr, 7) < 7 && strncmp(ref, bufrr, 6) != 0) + truncfail = true; + for (i = 7; i < sizeof(bufrr); i++) + if (bufrr[i] != (char)0xcc) { + truncfail = true; + break; + } + + if (truncfail) { + printf("truncation test FAILED:\n" + "fmt: \"%s\"\nref: \"%s\"\nfrr[:7]: \"%s\"\n%s\n\n", + fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); + errors++; + } + + struct fmt_outpos outpos[16]; + struct fbuf fb = { + .buf = bufrr, + .pos = bufrr, + .len = sizeof(bufrr) - 1, + .outpos = outpos, + .outpos_n = array_size(outpos), + }; + + va_start(ap, fmt); + vbprintfrr(&fb, fmt, ap); + fb.pos[0] = '\0'; + va_end(ap); + + printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n", + fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); + if (strcmp(ref, bufrr)) + errors++; + if (strlen(bufrr) != expectlen) { + printf("return value <> length mismatch\n"); + errors++; + } + + for (size_t i = 0; i < fb.outpos_i; i++) + printf("\t[%zu: %u..%u] = \"%.*s\"\n", i, + outpos[i].off_start, + outpos[i].off_end, + (int)(outpos[i].off_end - outpos[i].off_start), + bufrr + outpos[i].off_start); + printf("\n"); + return 0; +} + +static void test_va(const char *ref, const char *fmt, ...) PRINTFRR(2, 3); +static void test_va(const char *ref, const char *fmt, ...) +{ + struct va_format vaf; + va_list ap; + + va_start(ap, fmt); + vaf.fmt = fmt; + vaf.va = ≈ + + printchk(ref, "VA [%pVA] %s", &vaf, "--"); + + va_end(ap); +} + +int main(int argc, char **argv) +{ + size_t i; + float flts[] = { + 123.456789, + 23.456789e-30, + 3.456789e+30, + INFINITY, + NAN, + }; + uint64_t ui64 = 0xfeed1278cafef00d; + struct in_addr ip; + char *p; + char buf[256]; + + printcmp("%d %u %d %u", 123, 123, -456, -456); + printcmp("%lld %llu %lld %llu", 123LL, 123LL, -456LL, -456LL); + + printcmp("%-20s,%20s,%.20s", "test", "test", "test"); + printcmp("%-3s,%3s,%.3s", "test", "test", "test"); + printcmp("%-6.3s,%6.3s,%6.3s", "test", "test", "test"); + printcmp("%*s,%*s,%.*s", -3, "test", 3, "test", 3, "test"); + + for (i = 0; i < array_size(flts); i++) { + printcmp("%-6.3e,%6.3e,%+06.3e", flts[i], flts[i], flts[i]); + printcmp("%-6.3f,%6.3f,%+06.3f", flts[i], flts[i], flts[i]); + printcmp("%-6.3g,%6.3g,%+06.3g", flts[i], flts[i], flts[i]); + printcmp("%-6.3a,%6.3a,%+06.3a", flts[i], flts[i], flts[i]); + } + + printchk("-77385308584349683 18369358765125201933 feed1278cafef00d", + "%Ld %Lu %Lx", ui64, ui64, ui64); + + inet_aton("192.168.1.2", &ip); + printchk("192.168.1.2", "%pI4", &ip); + printchk(" 192.168.1.2", "%20pI4", &ip); + printchk("192.168.1.2 ", "%-20pI4", &ip); + + printcmp("%p", &ip); + + test_va("VA [192.168.1.2 1234] --", "%pI4 %u", &ip, 1234); + + inet_aton("0.0.0.0", &ip); + printchk("0.0.0.0", "%pI4", &ip); + printchk("*", "%pI4s", &ip); + + snprintfrr(buf, sizeof(buf), "test%s", "#1"); + csnprintfrr(buf, sizeof(buf), "test%s", "#2"); + assert(strcmp(buf, "test#1test#2") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), "test%s", "#3"); + assert(p == buf); + assert(strcmp(buf, "test#3") == 0); + + p = asnprintfrr(MTYPE_TMP, buf, 4, "test%s", "#4"); + assert(p != buf); + assert(strcmp(p, "test#4") == 0); + XFREE(MTYPE_TMP, p); + + p = asprintfrr(MTYPE_TMP, "test%s", "#5"); + assert(strcmp(p, "test#5") == 0); + XFREE(MTYPE_TMP, p); + + struct prefix pfx; + + str2prefix("192.168.1.23/24", &pfx); + printchk("192.168.1.23/24", "%pFX", &pfx); + printchk("192.168.1.23", "%pFXh", &pfx); + + str2prefix("2001:db8::1234/64", &pfx); + printchk("2001:db8::1234/64", "%pFX", &pfx); + printchk("2001:db8::1234", "%pFXh", &pfx); + + pfx.family = AF_UNIX; + printchk("UNK prefix", "%pFX", &pfx); + printchk("{prefix.af=AF_UNIX}", "%pFXh", &pfx); + + str2prefix_eth("02:ca:fe:f0:0d:1e/48", (struct prefix_eth *)&pfx); + printchk("02:ca:fe:f0:0d:1e/48", "%pFX", &pfx); + printchk("02:ca:fe:f0:0d:1e", "%pFXh", &pfx); + + struct prefix_sg sg; + sg.src.s_addr = INADDR_ANY; + sg.grp.s_addr = INADDR_ANY; + printchk("(*,*)", "%pPSG4", &sg); + + inet_aton("192.168.1.2", &sg.src); + printchk("(192.168.1.2,*)", "%pPSG4", &sg); + + inet_aton("224.1.2.3", &sg.grp); + printchk("(192.168.1.2,224.1.2.3)", "%pPSG4", &sg); + + sg.src.s_addr = INADDR_ANY; + printchk("(*,224.1.2.3)", "%pPSG4", &sg); + + uint8_t randhex[] = { 0x12, 0x34, 0x00, 0xca, 0xfe, 0x00, 0xaa, 0x55 }; + + FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.8pHX", randhex)); + FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.*pHX", + (int)sizeof(randhex), randhex)); + FMT_NSTD(printchk("12 34 00 ca", "%.4pHX", randhex)); + + printchk("12 34 00 ca fe 00 aa 55", "%8pHX", randhex); + printchk("12 34 00 ca fe 00 aa 55", "%*pHX", + (int)sizeof(randhex), randhex); + printchk("12 34 00 ca", "%4pHX", randhex); + + printchk("", "%pHX", randhex); + + printchk("12:34:00:ca:fe:00:aa:55", "%8pHXc", randhex); + printchk("123400cafe00aa55", "%8pHXn", randhex); + + printchk("/test/pa\\ th/\\~spe\\ncial\\x01/file.name", "%pSE", + "/test/pa th/~spe\ncial\x01/file.name"); + printchk("/test/pa\\ th/\\~spe\\n", "%17pSE", + "/test/pa th/~spe\ncial\x01/file.name"); + + char nulltest[] = { 'n', 'u', 0, 'l', 'l' }; + + printchk("nu\\x00ll", "%5pSE", nulltest); + printchk("nu\\x00ll", "%*pSE", 5, nulltest); + + printchk("bl\\\"ah\\x01te[st\\nab]c", "%pSQ", + "bl\"ah\x01te[st\nab]c"); + printchk("\"bl\\\"ah\\x01te[st\\nab]c\"", "%pSQq", + "bl\"ah\x01te[st\nab]c"); + printchk("\"bl\\\"ah\\x01te[st\\x0aab\\]c\"", "%pSQqs", + "bl\"ah\x01te[st\nab]c"); + printchk("\"\"", "%pSQqn", ""); + printchk("\"\"", "%pSQqn", (char *)NULL); + printchk("(null)", "%pSQq", (char *)NULL); + + /* + * %pNH<foo> tests + * + * gateway addresses only for now: interfaces require more setup + */ + printchk("(null)", "%pNHcg", (struct nexthop *)NULL); + printchk("(null)", "%pNHci", (struct nexthop *)NULL); + + struct nexthop nh; + + memset(&nh, 0, sizeof(nh)); + + nh.type = NEXTHOP_TYPE_IPV4; + inet_aton("3.2.1.0", &nh.gate.ipv4); + printchk("3.2.1.0", "%pNHcg", &nh); + + nh.type = NEXTHOP_TYPE_IPV6; + inet_pton(AF_INET6, "fe2c::34", &nh.gate.ipv6); + printchk("fe2c::34", "%pNHcg", &nh); + + /* time printing */ + + /* need a non-UTC timezone for testing */ + setenv("TZ", "TEST-01:00", 1); + tzset(); + + struct timespec ts; + struct timeval tv; + time_t tt; + + ts.tv_sec = tv.tv_sec = tt = 1642015880; + ts.tv_nsec = 123456789; + tv.tv_usec = 234567; + + printchk("Wed Jan 12 20:31:20 2022", "%pTSR", &ts); + printchk("Wed Jan 12 20:31:20 2022", "%pTVR", &tv); + printchk("Wed Jan 12 20:31:20 2022", "%pTTR", &tt); + + FMT_NSTD(printchk("Wed Jan 12 20:31:20 2022", "%.3pTSR", &ts)); + + printchk("2022-01-12T20:31:20.123", "%pTSRi", &ts); + printchk("2022-01-12 20:31:20.123", "%pTSRip", &ts); + printchk("2022-01-12 20:31:20.123", "%pTSRpi", &ts); + FMT_NSTD(printchk("2022-01-12T20:31:20", "%.0pTSRi", &ts)); + FMT_NSTD(printchk("2022-01-12T20:31:20.123456789", "%.9pTSRi", &ts)); + FMT_NSTD(printchk("2022-01-12T20:31:20", "%.3pTTRi", &tt)); + + ts.tv_sec = tv.tv_sec = tt = 9 * 86400 + 12345; + + printchk("1w 2d 03:25:45.123", "%pTSIp", &ts); + printchk("1w2d03:25:45.123", "%pTSI", &ts); + printchk("1w2d03:25:45.234", "%pTVI", &tv); + printchk("1w2d03:25:45", "%pTTI", &tt); + + printchk("1w 2d 03h", "%pTVItp", &tv); + printchk("1w2d03h", "%pTSIt", &ts); + + printchk("219:25:45", "%pTVIh", &tv); + printchk("13165:45", "%pTVIm", &tv); + + ts.tv_sec = tv.tv_sec = tt = 1 * 86400 + 12345; + + printchk("1d 03:25:45.123", "%pTSIp", &ts); + printchk("1d03:25:45.234", "%pTVI", &tv); + + printchk("1d 03h 25m", "%pTVItp", &tv); + printchk("1d03h25m", "%pTSIt", &ts); + + printchk("98745.234", "%pTVId", &tv); + + printchk("27:25:45", "%pTVIh", &tv); + printchk("1645:45", "%pTVIm", &tv); + + ts.tv_sec = tv.tv_sec = tt = 12345; + + printchk("03:25:45.123", "%pTSIp", &ts); + printchk("03:25:45.123", "%pTSI", &ts); + printchk("03:25:45.234", "%pTVI", &tv); + printchk("03:25:45", "%pTTI", &tt); + + printchk("03:25:45", "%pTSItp", &ts); + printchk("03:25:45", "%pTVIt", &tv); + + printchk("12345.234", "%pTVId", &tv); + + printchk("03:25:45", "%pTVIh", &tv); + printchk("205:45", "%pTVIm", &tv); + + ts.tv_sec = tv.tv_sec = tt = 0; + + printchk("00:00:00.123", "%pTSIp", &ts); + printchk("00:00:00.123", "%pTSI", &ts); + printchk("00:00:00.234", "%pTVI", &tv); + printchk("00:00:00", "%pTTI", &tt); + + printchk("00:00:00", "%pTVItp", &tv); + printchk("00:00:00", "%pTSIt", &ts); + + printchk("0.234", "%pTVId", &tv); + printchk("0.234", "%pTVIdx", &tv); + printchk("-", "%pTTIdx", &tt); + + printchk("00:00:00", "%pTVIhx", &tv); + printchk("--:--:--", "%pTTIhx", &tt); + printchk("00:00", "%pTVImx", &tv); + printchk("--:--", "%pTTImx", &tt); + + ts.tv_sec = tv.tv_sec = tt = -10; + + printchk("-00:00:09.876", "%pTSIp", &ts); + printchk("-00:00:09.876", "%pTSI", &ts); + printchk("-00:00:09.765", "%pTVI", &tv); + printchk("-00:00:10", "%pTTI", &tt); + + printchk("-00:00:09", "%pTSItp", &ts); + printchk("-00:00:09", "%pTSIt", &ts); + printchk("-00:00:09", "%pTVIt", &tv); + printchk("-00:00:10", "%pTTIt", &tt); + + printchk("-9.765", "%pTVId", &tv); + printchk("-", "%pTVIdx", &tv); + + printchk("-00:00:09", "%pTSIh", &ts); + printchk("--:--:--", "%pTVIhx", &tv); + printchk("--:--:--", "%pTTIhx", &tt); + + printchk("-00:09", "%pTSIm", &ts); + printchk("--:--", "%pTVImx", &tv); + printchk("--:--", "%pTTImx", &tt); + + return !!errors; +} diff --git a/tests/lib/test_printfrr.py b/tests/lib/test_printfrr.py new file mode 100644 index 0000000..b8ab89e --- /dev/null +++ b/tests/lib/test_printfrr.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestPrintfrr(frrtest.TestMultiOut): + program = "./test_printfrr" + + +TestPrintfrr.exit_cleanly() diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c new file mode 100644 index 0000000..c06ebbe --- /dev/null +++ b/tests/lib/test_privs.c @@ -0,0 +1,137 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include <lib/version.h> +#include "getopt.h" +#include "privs.h" +#include "memory.h" +#include "lib_vty.h" + +zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_DAC_OVERRIDE, +}; + +struct zebra_privs_t test_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +struct option longopts[] = {{"help", no_argument, NULL, 'h'}, + {"user", required_argument, NULL, 'u'}, + {"group", required_argument, NULL, 'g'}, + {0}}; + +/* Help information display. */ +static void usage(char *progname, int status) +{ + if (status != 0) + fprintf(stderr, "Try `%s --help' for more information.\n", + progname); + else { + printf("Usage : %s [OPTION...]\n\ +Daemon which does 'slow' things.\n\n\ +-u, --user User to run as\n\ +-g, --group Group to run as\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", + progname, FRR_BUG_ADDRESS); + } + exit(status); +} + +struct thread_master *master; +/* main routine. */ +int main(int argc, char **argv) +{ + char *p; + char *progname; + struct zprivs_ids_t ids; + + /* Set umask before anything for security */ + umask(0027); + + /* get program name */ + progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); + + while (1) { + int opt; + + opt = getopt_long(argc, argv, "hu:g:", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 'u': + test_privs.user = optarg; + break; + case 'g': + test_privs.group = optarg; + break; + case 'h': + usage(progname, 0); + break; + default: + usage(progname, 1); + break; + } + } + + /* Library inits. */ + lib_cmd_init(); + zprivs_preinit(&test_privs); + zprivs_init(&test_privs); + +#define PRIV_STATE() \ + ((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered") + + printf("%s\n", PRIV_STATE()); + frr_with_privs(&test_privs) { + printf("%s\n", PRIV_STATE()); + } + + printf("%s\n", PRIV_STATE()); + zprivs_get_ids(&ids); + + /* terminate privileges */ + zprivs_terminate(&test_privs); + + /* but these should continue to work... */ + printf("%s\n", PRIV_STATE()); + frr_with_privs(&test_privs) { + printf("%s\n", PRIV_STATE()); + } + + printf("%s\n", PRIV_STATE()); + zprivs_get_ids(&ids); + + printf("terminating\n"); + return 0; +} diff --git a/tests/lib/test_resolver.c b/tests/lib/test_resolver.c new file mode 100644 index 0000000..6a582cc --- /dev/null +++ b/tests/lib/test_resolver.c @@ -0,0 +1,81 @@ +/* + * FRR c-ares integration test + * Copyright (C) 2021 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* this test is not run automatically since tests MUST NOT rely on any outside + * state. DNS is most definitely "outside state". A testbed may not have any + * internet connectivity at all. It may not have working DNS. Or worst of + * all, whatever name we use to test may have a temporary failure entirely + * beyond our control. + * + * The only way this test could be run in a testbed is with an all-local DNS + * setup, which considering the resolver code is rarely touched is not worth + * the time at all. Instead, after touching the resolver code, manually run + * this test and throw some names at it. + */ + +#include <zebra.h> + +#include "vty.h" +#include "command.h" +#include "resolver.h" +#include "log.h" +#include "sockunion.h" + +#include "tests/lib/cli/common_cli.h" + +extern struct thread_master *master; + +static void resolver_result(struct resolver_query *resq, const char *errstr, + int numaddrs, union sockunion *addr) +{ + int i; + + if (numaddrs <= 0) { + zlog_warn("hostname resolution failed: %s", errstr); + return; + } + + for (i = 0; i < numaddrs; i++) + zlog_info("resolver result: %pSU", &addr[i]); +} + +struct resolver_query query; + +DEFUN (test_resolve, + test_resolve_cmd, + "resolve WORD", + "DNS resolver\n" + "Name to resolve\n") +{ + resolver_resolve(&query, AF_UNSPEC, 0, argv[1]->arg, resolver_result); + return CMD_SUCCESS; +} + +__attribute__((_CONSTRUCTOR(2000))) +static void test_setup(void) +{ + test_log_prio = LOG_DEBUG; +} + +void test_init(int argc, char **argv) +{ + resolver_init(master); + + install_element(VIEW_NODE, &test_resolve_cmd); +} diff --git a/tests/lib/test_ringbuf.c b/tests/lib/test_ringbuf.c new file mode 100644 index 0000000..4ac6294 --- /dev/null +++ b/tests/lib/test_ringbuf.c @@ -0,0 +1,190 @@ +/* + * Circular buffer tests. + * Copyright (C) 2017 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <memory.h> +#include "ringbuf.h" + +static void validate_state(struct ringbuf *buf, size_t size, size_t contains) +{ + assert(buf->size == size); + assert(ringbuf_remain(buf) == contains); + assert(ringbuf_space(buf) == buf->size - contains); + assert(buf->empty != (bool)contains); +} + +int main(int argc, char **argv) +{ + struct ringbuf *soil = ringbuf_new(BUFSIZ); + + validate_state(soil, BUFSIZ, 0); + + /* verify reset functionality on clean buffer */ + printf("Validating reset on empty buffer...\n"); + ringbuf_reset(soil); + + validate_state(soil, BUFSIZ, 0); + + /* put one byte */ + printf("Validating write...\n"); + uint8_t walnut = 47; + assert(ringbuf_put(soil, &walnut, sizeof(walnut)) == 1); + + validate_state(soil, BUFSIZ, 1); + + /* validate read limitations */ + printf("Validating read limits...\n"); + uint8_t nuts[2]; + assert(ringbuf_get(soil, &nuts, sizeof(nuts)) == 1); + + /* reset */ + printf("Validating reset on full buffer...\n"); + ringbuf_reset(soil); + validate_state(soil, BUFSIZ, 0); + + /* copy stack garbage to buffer */ + printf("Validating big write...\n"); + uint8_t compost[BUFSIZ]; + assert(ringbuf_put(soil, &compost, sizeof(compost)) == BUFSIZ); + + validate_state(soil, BUFSIZ, BUFSIZ); + assert(soil->start == 0); + assert(soil->end == 0); + + /* read 15 bytes of garbage */ + printf("Validating read...\n"); + assert(ringbuf_get(soil, &compost, 15) == 15); + + validate_state(soil, BUFSIZ, BUFSIZ - 15); + assert(soil->start == 15); + assert(soil->end == 0); + + /* put another 10 bytes and validate wraparound */ + printf("Validating wraparound...\n"); + assert(ringbuf_put(soil, &compost[BUFSIZ/2], 10) == 10); + + validate_state(soil, BUFSIZ, BUFSIZ - 15 + 10); + assert(soil->start == 15); + assert(soil->end == 10); + + /* put another 15 bytes and validate state */ + printf("Validating size limits...\n"); + assert(ringbuf_put(soil, &compost, 15) == 5); + validate_state(soil, BUFSIZ, BUFSIZ); + + /* read entire buffer */ + printf("Validating big read...\n"); + assert(ringbuf_get(soil, &compost, BUFSIZ) == BUFSIZ); + + validate_state(soil, BUFSIZ, 0); + assert(soil->empty == true); + assert(soil->start == soil->end); + assert(soil->start == 15); + + /* read empty buffer */ + printf("Validating empty read...\n"); + assert(ringbuf_get(soil, &compost, 1) == 0); + validate_state(soil, BUFSIZ, 0); + + /* reset, validate state */ + printf("Validating reset...\n"); + ringbuf_reset(soil); + validate_state(soil, BUFSIZ, 0); + assert(soil->start == 0); + assert(soil->end == 0); + + /* wipe, validate state */ + printf("Validating wipe...\n"); + memset(&compost, 0x00, sizeof(compost)); + ringbuf_wipe(soil); + assert(memcmp(&compost, soil->data, sizeof(compost)) == 0); + + /* validate maximum write */ + printf("Validating very big write...\n"); + const char flower[BUFSIZ * 2]; + assert(ringbuf_put(soil, &flower, sizeof(flower)) == BUFSIZ); + + validate_state(soil, BUFSIZ, BUFSIZ); + + /* wipe, validate state */ + printf("Validating wipe...\n"); + memset(&compost, 0x00, sizeof(compost)); + ringbuf_wipe(soil); + assert(memcmp(&compost, soil->data, sizeof(compost)) == 0); + + /* validate simple data encode / decode */ + const char *organ = "seed"; + printf("Encoding: '%s'\n", organ); + assert(ringbuf_put(soil, organ, strlen(organ)) == 4); + char water[strlen(organ) + 1]; + assert(ringbuf_get(soil, &water, strlen(organ)) == 4); + water[strlen(organ)] = '\0'; + printf("Retrieved: '%s'\n", water); + + validate_state(soil, BUFSIZ, 0); + + /* validate simple data encode / decode across ring boundary */ + soil->start = soil->size - 2; + soil->end = soil->start; + const char *phloem = "root"; + printf("Encoding: '%s'\n", phloem); + assert(ringbuf_put(soil, phloem, strlen(phloem)) == 4); + char xylem[strlen(phloem) + 1]; + assert(ringbuf_get(soil, &xylem, 100) == 4); + xylem[strlen(phloem)] = '\0'; + printf("Retrieved: '%s'\n", xylem); + + ringbuf_wipe(soil); + + /* validate simple data peek across ring boundary */ + soil->start = soil->size - 2; + soil->end = soil->start; + const char *cytoplasm = "tree"; + printf("Encoding: '%s'\n", cytoplasm); + assert(ringbuf_put(soil, cytoplasm, strlen(cytoplasm)) == 4); + char chloroplast[strlen(cytoplasm) + 1]; + assert(ringbuf_peek(soil, 2, &chloroplast[0], 100) == 2); + assert(ringbuf_peek(soil, 0, &chloroplast[2], 2) == 2); + chloroplast[strlen(cytoplasm)] = '\0'; + assert(!strcmp(chloroplast, "eetr")); + printf("Retrieved: '%s'\n", chloroplast); + + printf("Deleting...\n"); + ringbuf_del(soil); + + printf("Creating new buffer...\n"); + soil = ringbuf_new(15); + soil->start = soil->end = 7; + + /* validate data encode of excessive data */ + const char *twenty = "vascular plants----"; + char sixteen[16]; + printf("Encoding: %s\n", twenty); + assert(ringbuf_put(soil, twenty, strlen(twenty)) == 15); + assert(ringbuf_get(soil, sixteen, 20)); + sixteen[15] = '\0'; + printf("Retrieved: %s\n", sixteen); + assert(!strcmp(sixteen, "vascular plants")); + + printf("Deleting...\n"); + ringbuf_del(soil); + + printf("Done.\n"); + return 0; +} diff --git a/tests/lib/test_ringbuf.py b/tests/lib/test_ringbuf.py new file mode 100644 index 0000000..0cd9dee --- /dev/null +++ b/tests/lib/test_ringbuf.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestRingbuf(frrtest.TestMultiOut): + program = "./test_ringbuf" + + +TestRingbuf.exit_cleanly() diff --git a/tests/lib/test_segv.c b/tests/lib/test_segv.c new file mode 100644 index 0000000..92c9f7a --- /dev/null +++ b/tests/lib/test_segv.c @@ -0,0 +1,80 @@ +/* + * SEGV / backtrace handling test. + * + * copied from test-sig.c + * + * Copyright (C) 2013 by David Lamparter, Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <sigevent.h> +#include "lib/log.h" +#include "lib/memory.h" + +struct frr_signal_t sigs[] = {}; + +struct thread_master *master; + +void func1(int *arg); +void func3(void); + +void func1(int *arg) +{ + int *null = NULL; + *null += 1; + *arg = 1; +} + +static void func2(size_t depth, int *arg) +{ + /* variable stack frame size */ + int buf[depth]; + for (size_t i = 0; i < depth; i++) + buf[i] = arg[i] + 1; + if (depth > 0) + func2(depth - 1, buf); + else + func1(&buf[0]); + for (size_t i = 0; i < depth; i++) + buf[i] = arg[i] + 2; +} + +void func3(void) +{ + int buf[6]; + func2(6, buf); +} + +static void threadfunc(struct thread *thread) +{ + func3(); +} + +int main(void) +{ + master = thread_master_create(NULL); + signal_init(master, array_size(sigs), sigs); + + zlog_aux_init("NONE: ", LOG_DEBUG); + + thread_execute(master, threadfunc, 0, 0); + + exit(0); +} diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c new file mode 100644 index 0000000..768307d --- /dev/null +++ b/tests/lib/test_seqlock.c @@ -0,0 +1,127 @@ +/* + * basic test for seqlock + * + * Copyright (C) 2015 David Lamparter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include <sys/uio.h> + +#include "monotime.h" +#include "seqlock.h" +#include "printfrr.h" + +static struct seqlock sqlo; +static pthread_t thr1; +static struct timeval start; + +static void writestr(const char *str) +{ + struct iovec iov[2]; + char buf[32]; + int64_t usec = monotime_since(&start, NULL); + + snprintfrr(buf, sizeof(buf), "[%02" PRId64 "] ", usec / 100000); + + iov[0].iov_base = buf; + iov[0].iov_len = strlen(buf); + iov[1].iov_base = (char *)str; + iov[1].iov_len = strlen(str); + writev(1, iov, 2); +} + +static void *thr1func(void *arg) +{ + assert(!seqlock_held(&sqlo)); + assert(seqlock_check(&sqlo, 1)); + seqlock_wait(&sqlo, 1); + writestr("thr1 (unheld)\n"); + + sleep(2); + + assert(seqlock_held(&sqlo)); + assert(seqlock_check(&sqlo, 1)); + seqlock_wait(&sqlo, 1); + writestr("thr1 @1\n"); + + seqlock_wait(&sqlo, 5); + writestr("thr1 @5\n"); + + seqlock_wait(&sqlo, 9); + writestr("thr1 @9\n"); + + seqlock_wait(&sqlo, 13); + writestr("thr1 @13\n"); + + seqlock_wait(&sqlo, 17); + writestr("thr1 @17\n"); + + seqlock_wait(&sqlo, 21); + writestr("thr1 @21\n"); + return NULL; +} + +int main(int argc, char **argv) +{ + monotime(&start); + + seqlock_init(&sqlo); + + assert(!seqlock_held(&sqlo)); + seqlock_acquire_val(&sqlo, 1); + assert(seqlock_held(&sqlo)); + + assert(seqlock_cur(&sqlo) == 1); + assert(seqlock_bump(&sqlo) == 1); + assert(seqlock_cur(&sqlo) == 5); + assert(seqlock_bump(&sqlo) == 5); + assert(seqlock_bump(&sqlo) == 9); + assert(seqlock_bump(&sqlo) == 13); + assert(seqlock_cur(&sqlo) == 17); + + assert(seqlock_held(&sqlo)); + seqlock_release(&sqlo); + assert(!seqlock_held(&sqlo)); + + pthread_create(&thr1, NULL, thr1func, NULL); + sleep(1); + + writestr("main @5\n"); + seqlock_acquire_val(&sqlo, 5); + sleep(2); + + writestr("main @9\n"); + seqlock_bump(&sqlo); + sleep(1); + + writestr("main @17\n"); + seqlock_acquire_val(&sqlo, 17); + sleep(1); + + writestr("main @release\n"); + seqlock_release(&sqlo); + sleep(1); +} diff --git a/tests/lib/test_sig.c b/tests/lib/test_sig.c new file mode 100644 index 0000000..9165f18 --- /dev/null +++ b/tests/lib/test_sig.c @@ -0,0 +1,66 @@ +/* + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <sigevent.h> +#include "lib/log.h" +#include "lib/memory.h" + +static void sighup(void) +{ + printf("processed hup\n"); +} + +static void sigusr1(void) +{ + printf("processed usr1\n"); +} + +static void sigusr2(void) +{ + printf("processed usr2\n"); +} + +struct frr_signal_t sigs[] = {{ + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGUSR2, + .handler = &sigusr2, + }}; + +struct thread_master *master; +struct thread t; + +int main(void) +{ + master = thread_master_create(NULL); + signal_init(master, array_size(sigs), sigs); + + zlog_aux_init("NONE: ", LOG_DEBUG); + + while (thread_fetch(master, &t)) + thread_call(&t); + + exit(0); +} diff --git a/tests/lib/test_skiplist.c b/tests/lib/test_skiplist.c new file mode 100644 index 0000000..2f9ca5e --- /dev/null +++ b/tests/lib/test_skiplist.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021, LabN Consulting, L.L.C + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <skiplist.h> + +static void sl_debug(struct skiplist *l) +{ + int i; + + if (!l) + return; + + printf("Skiplist %p has max level %d\n", l, l->level); + for (i = l->level; i >= 0; --i) + printf(" @%d: %d\n", i, l->level_stats[i]); +} + +static void *scramble(int i) +{ + uintptr_t result; + + result = (uintptr_t)(i & 0xff) << 24; + result |= (uintptr_t)i >> 8; + + return (void *)result; +} +#define sampleSize 65536 +static int sl_test(void) +{ + struct skiplist *l; + register int i, k; + void *keys[sampleSize]; + void *v = NULL; + int errors = 0; + + l = skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, NULL, NULL); + + printf("%s: skiplist_new returned %p\n", __func__, l); + + for (i = 0; i < 4; i++) { + + for (k = 0; k < sampleSize; k++) { + if (!(k % 10000)) + printf("%s: (%d:%d)\n", __func__, i, k); + /* keys[k] = (void *)random(); */ + keys[k] = scramble(k); + if (skiplist_insert(l, keys[k], keys[k])) { + ++errors; + printf("error in insert #%d,#%d\n", i, k); + } + } + + printf("%s: inserts done\n", __func__); + sl_debug(l); + + for (k = 0; k < sampleSize; k++) { + + if (!(k % 10000)) + printf("[%d:%d]\n", i, k); + /* keys[k] = (void *)random(); */ + if (skiplist_search(l, keys[k], &v)) { + ++errors; + printf("error in search #%d,#%d\n", i, k); + } + + if (v != keys[k]) { + ++errors; + printf("search returned wrong value\n"); + } + } + printf("%s: searches done\n", __func__); + + + for (k = 0; k < sampleSize; k++) { + + if (!(k % 10000)) + printf("<%d:%d>\n", i, k); + /* keys[k] = (void *)random(); */ + if (skiplist_delete(l, keys[k], keys[k])) { + ++errors; + printf("error in delete\n"); + } + keys[k] = scramble(k ^ 0xf0f0f0f0); + if (skiplist_insert(l, keys[k], keys[k])) { + ++errors; + printf("error in insert #%d,#%d\n", i, k); + } + } + + printf("%s: del+inserts done\n", __func__); + sl_debug(l); + + for (k = 0; k < sampleSize; k++) { + + if (!(k % 10000)) + printf("{%d:%d}\n", i, k); + /* keys[k] = (void *)random(); */ + if (skiplist_delete_first(l)) { + ++errors; + printf("error in delete_first\n"); + } + } + } + + sl_debug(l); + + skiplist_free(l); + + return errors; +} + +int main(int argc, char **argv) +{ + int errors = sl_test(); + + if (errors) + return 1; + return 0; +} diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c new file mode 100644 index 0000000..2d8f024 --- /dev/null +++ b/tests/lib/test_srcdest_table.c @@ -0,0 +1,436 @@ +/* + * Test srcdest table for correctness. + * + * Copyright (C) 2017 by David Lamparter & Christian Franke, + * Open Source Routing / NetDEF Inc. + * + * This file is part of FRRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "hash.h" +#include "memory.h" +#include "prefix.h" +#include "prng.h" +#include "srcdest_table.h" +#include "table.h" + +/* Copied from ripngd/ripng_nexthop.h - maybe the whole s6_addr32 thing + * should be added by autoconf if not present? + */ +#ifndef s6_addr32 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif /*s6_addr32*/ + +struct thread_master *master; + +/* This structure is copied from lib/srcdest_table.c to which it is + * private as far as other parts of Quagga are concerned. + */ +struct srcdest_rnode { + /* must be first in structure for casting to/from route_node */ + ROUTE_NODE_FIELDS; + + struct route_table *src_table; +}; + +struct test_state { + struct route_table *table; + struct hash *log; +}; + +static char *format_srcdest(const struct prefix_ipv6 *dst_p, + const struct prefix_ipv6 *src_p) +{ + char dst_str[BUFSIZ]; + char src_str[BUFSIZ]; + char *rv; + int ec; + + prefix2str((const struct prefix *)dst_p, dst_str, sizeof(dst_str)); + if (src_p && src_p->prefixlen) + prefix2str((const struct prefix *)src_p, src_str, + sizeof(src_str)); + else + src_str[0] = '\0'; + + ec = asprintf(&rv, "%s%s%s", dst_str, + (src_str[0] != '\0') ? " from " : "", src_str); + + assert(ec > 0); + return rv; +} + +static unsigned int log_key(const void *data) +{ + const struct prefix *hash_entry = data; + struct prefix_ipv6 *dst_p = (struct prefix_ipv6 *)&hash_entry[0]; + struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1]; + unsigned int hash = 0; + unsigned int i; + + hash = (hash * 33) ^ (unsigned int)dst_p->prefixlen; + for (i = 0; i < 4; i++) + hash = (hash * 33) ^ (unsigned int)dst_p->prefix.s6_addr32[i]; + + hash = (hash * 33) ^ (unsigned int)src_p->prefixlen; + if (src_p->prefixlen) + for (i = 0; i < 4; i++) + hash = (hash * 33) + ^ (unsigned int)src_p->prefix.s6_addr32[i]; + + return hash; +} + +static bool log_cmp(const void *a, const void *b) +{ + if (a == NULL || b == NULL) + return false; + + return !memcmp(a, b, 2 * sizeof(struct prefix)); +} + +static void log_free(void *data) +{ + XFREE(MTYPE_TMP, data); +} + +static void *log_alloc(void *data) +{ + void *rv = XMALLOC(MTYPE_TMP, 2 * sizeof(struct prefix)); + memcpy(rv, data, 2 * sizeof(struct prefix)); + return rv; +} + +static struct test_state *test_state_new(void) +{ + struct test_state *rv; + + rv = XCALLOC(MTYPE_TMP, sizeof(*rv)); + assert(rv); + + rv->table = srcdest_table_init(); + assert(rv->table); + + rv->log = hash_create(log_key, log_cmp, NULL); + return rv; +} + +static void test_state_free(struct test_state *test) +{ + route_table_finish(test->table); + hash_clean(test->log, log_free); + hash_free(test->log); + XFREE(MTYPE_TMP, test); +} + +static void test_state_add_route(struct test_state *test, + struct prefix_ipv6 *dst_p, + struct prefix_ipv6 *src_p) +{ + struct route_node *rn = + srcdest_rnode_get(test->table, (struct prefix *)dst_p, src_p); + struct prefix hash_entry[2]; + + memset(hash_entry, 0, sizeof(hash_entry)); + memcpy(&hash_entry[0], dst_p, sizeof(*dst_p)); + memcpy(&hash_entry[1], src_p, sizeof(*src_p)); + + if (rn->info) { + route_unlock_node(rn); + assert(hash_lookup(test->log, hash_entry) != NULL); + return; + } else { + assert(hash_lookup(test->log, hash_entry) == NULL); + } + + rn->info = (void *)0xdeadbeef; + (void)hash_get(test->log, hash_entry, log_alloc); +}; + +static void test_state_del_route(struct test_state *test, + struct prefix_ipv6 *dst_p, + struct prefix_ipv6 *src_p) +{ + struct route_node *rn = srcdest_rnode_lookup( + test->table, (struct prefix *)dst_p, src_p); + struct prefix hash_entry[2]; + + memset(hash_entry, 0, sizeof(hash_entry)); + memcpy(&hash_entry[0], dst_p, sizeof(*dst_p)); + memcpy(&hash_entry[1], src_p, sizeof(*src_p)); + + if (!rn) { + assert(!hash_lookup(test->log, hash_entry)); + return; + } + + assert(rn->info == (void *)0xdeadbeef); + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + + struct prefix *hash_entry_intern = hash_release(test->log, hash_entry); + assert(hash_entry_intern != NULL); + XFREE(MTYPE_TMP, hash_entry_intern); +} + +static void verify_log(struct hash_bucket *bucket, void *arg) +{ + struct test_state *test = arg; + struct prefix *hash_entry = bucket->data; + struct prefix *dst_p = &hash_entry[0]; + struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1]; + struct route_node *rn = srcdest_rnode_lookup(test->table, dst_p, src_p); + + assert(rn); + assert(rn->info == (void *)0xdeadbeef); + + route_unlock_node(rn); +} + +static void dump_log(struct hash_bucket *bucket, void *arg) +{ + struct prefix *hash_entry = bucket->data; + struct prefix_ipv6 *dst_p = (struct prefix_ipv6 *)&hash_entry[0]; + struct prefix_ipv6 *src_p = (struct prefix_ipv6 *)&hash_entry[1]; + char *route_id = format_srcdest(dst_p, src_p); + + fprintf(stderr, " %s\n", route_id); + free(route_id); +} + +static void test_dump(struct test_state *test) +{ + fprintf(stderr, "Contents of hash table:\n"); + hash_iterate(test->log, dump_log, test); + fprintf(stderr, "\n"); +} + +static void test_failed(struct test_state *test, const char *message, + const struct prefix_ipv6 *dst_p, + const struct prefix_ipv6 *src_p) +{ + char *route_id = format_srcdest(dst_p, src_p); + + fprintf(stderr, "Test failed. Error: %s\n", message); + fprintf(stderr, "Route in question: %s\n", route_id); + free(route_id); + + test_dump(test); + assert(3 == 4); +} + +static void test_state_verify(struct test_state *test) +{ + struct route_node *rn; + struct prefix hash_entry[2]; + + memset(hash_entry, 0, sizeof(hash_entry)); + + /* Verify that there are no elements in the table which have never + * been added */ + for (rn = route_top(test->table); rn; rn = srcdest_route_next(rn)) { + const struct prefix_ipv6 *dst_p, *src_p; + + /* While we are iterating, we hold a lock on the current + * route_node, + * so all the lock counts we check for take that into account; + * in idle + * state all the numbers will be exactly one less. + * + * Also this makes quite some assumptions based on the current + * implementation details of route_table and srcdest_table - + * another + * valid implementation might trigger assertions here. + */ + + if (rnode_is_dstnode(rn)) { + struct srcdest_rnode *srn = (struct srcdest_rnode *)rn; + unsigned int expected_lock = 1; /* We are in the loop */ + + if (rn->info + != NULL) /* The route node is not internal */ + expected_lock++; + if (srn->src_table != NULL) /* There's a source table + associated with rn */ + expected_lock++; + + if (route_node_get_lock_count(rn) != expected_lock) + test_failed( + test, + "Dest rnode lock count doesn't match expected count!", + (struct prefix_ipv6 *)&rn->p, NULL); + } else { + unsigned int expected_lock = 1; /* We are in the loop */ + + if (rn->info + != NULL) /* The route node is not internal */ + expected_lock++; + + if (route_node_get_lock_count(rn) != expected_lock) { + srcdest_rnode_prefixes( + rn, (const struct prefix **)&dst_p, + (const struct prefix **)&src_p); + + test_failed( + test, + "Src rnode lock count doesn't match expected count!", + dst_p, src_p); + } + } + + if (!rn->info) + continue; + + assert(rn->info == (void *)0xdeadbeef); + + srcdest_rnode_prefixes(rn, (const struct prefix **)&dst_p, + (const struct prefix **)&src_p); + memcpy(&hash_entry[0], dst_p, sizeof(*dst_p)); + if (src_p) + memcpy(&hash_entry[1], src_p, sizeof(*src_p)); + else + memset(&hash_entry[1], 0, sizeof(hash_entry[1])); + + if (hash_lookup(test->log, hash_entry) == NULL) + test_failed(test, "Route is missing in hash", dst_p, + src_p); + } + + /* Verify that all added elements are still in the table */ + hash_iterate(test->log, verify_log, test); +} + +static void get_rand_prefix(struct prng *prng, struct prefix_ipv6 *p) +{ + int i; + + memset(p, 0, sizeof(*p)); + + for (i = 0; i < 4; i++) + p->prefix.s6_addr32[i] = prng_rand(prng); + p->prefixlen = prng_rand(prng) % 129; + p->family = AF_INET6; + + apply_mask(p); +} + +static void get_rand_prefix_pair(struct prng *prng, struct prefix_ipv6 *dst_p, + struct prefix_ipv6 *src_p) +{ + get_rand_prefix(prng, dst_p); + if ((prng_rand(prng) % 4) == 0) { + get_rand_prefix(prng, src_p); + if (src_p->prefixlen) + return; + } + + memset(src_p, 0, sizeof(*src_p)); +} + +static void test_state_add_rand_route(struct test_state *test, + struct prng *prng) +{ + struct prefix_ipv6 dst_p, src_p; + + get_rand_prefix_pair(prng, &dst_p, &src_p); + test_state_add_route(test, &dst_p, &src_p); +} + +static void test_state_del_rand_route(struct test_state *test, + struct prng *prng) +{ + struct prefix_ipv6 dst_p, src_p; + + get_rand_prefix_pair(prng, &dst_p, &src_p); + test_state_del_route(test, &dst_p, &src_p); +} + +static void test_state_del_one_route(struct test_state *test, struct prng *prng) +{ + unsigned int which_route; + + if (test->log->count == 0) + return; + + which_route = prng_rand(prng) % test->log->count; + + struct route_node *rn; + const struct prefix *dst_p, *src_p; + struct prefix_ipv6 dst6_p, src6_p; + + for (rn = route_top(test->table); rn; rn = srcdest_route_next(rn)) { + if (!rn->info) + continue; + if (!which_route) { + route_unlock_node(rn); + break; + } + which_route--; + } + + assert(rn); + srcdest_rnode_prefixes(rn, &dst_p, &src_p); + memcpy(&dst6_p, dst_p, sizeof(dst6_p)); + if (src_p) + memcpy(&src6_p, src_p, sizeof(src6_p)); + else + memset(&src6_p, 0, sizeof(src6_p)); + + test_state_del_route(test, &dst6_p, &src6_p); +} + +static void run_prng_test(void) +{ + struct test_state *test = test_state_new(); + struct prng *prng = prng_new(0); + size_t i; + + for (i = 0; i < 1000; i++) { + switch (prng_rand(prng) % 10) { + case 0: + case 1: + case 2: + case 3: + case 4: + test_state_add_rand_route(test, prng); + break; + case 5: + case 6: + case 7: + test_state_del_one_route(test, prng); + break; + case 8: + case 9: + test_state_del_rand_route(test, prng); + break; + } + test_state_verify(test); + } + + prng_free(prng); + test_state_free(test); +} + +int main(int argc, char *argv[]) +{ + run_prng_test(); + printf("PRNG Test successful.\n"); + return 0; +} diff --git a/tests/lib/test_srcdest_table.py b/tests/lib/test_srcdest_table.py new file mode 100644 index 0000000..d0dde6a --- /dev/null +++ b/tests/lib/test_srcdest_table.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestSrcdestTable(frrtest.TestMultiOut): + program = "./test_srcdest_table" + + +TestSrcdestTable.onesimple("PRNG Test successful.") diff --git a/tests/lib/test_stream.c b/tests/lib/test_stream.c new file mode 100644 index 0000000..a45c2b4 --- /dev/null +++ b/tests/lib/test_stream.c @@ -0,0 +1,72 @@ +/* Simple stream test. + * + * Copyright (C) 2006 Sun Microsystems, Inc. + * + * This file is part of Quagga. + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include <stream.h> +#include <thread.h> + +#include "printfrr.h" + +static unsigned long long ham = 0xdeadbeefdeadbeef; +struct thread_master *master; + +static void print_stream(struct stream *s) +{ + size_t getp = stream_get_getp(s); + + printfrr("endp: %zu, readable: %zu, writeable: %zu\n", + stream_get_endp(s), STREAM_READABLE(s), STREAM_WRITEABLE(s)); + + while (STREAM_READABLE(s)) { + printfrr("0x%x ", *stream_pnt(s)); + stream_forward_getp(s, 1); + } + + printfrr("\n"); + + /* put getp back to where it was */ + stream_set_getp(s, getp); +} + +int main(void) +{ + struct stream *s; + + s = stream_new(1024); + + stream_putc(s, ham); + stream_putw(s, ham); + stream_putl(s, ham); + stream_putq(s, ham); + + print_stream(s); + + stream_resize_inplace(&s, stream_get_endp(s)); + + print_stream(s); + + printfrr("c: 0x%hhx\n", stream_getc(s)); + printfrr("w: 0x%hx\n", stream_getw(s)); + printfrr("l: 0x%x\n", stream_getl(s)); + printfrr("q: 0x%" PRIx64 "\n", stream_getq(s)); + + return 0; +} diff --git a/tests/lib/test_stream.py b/tests/lib/test_stream.py new file mode 100644 index 0000000..11d902e --- /dev/null +++ b/tests/lib/test_stream.py @@ -0,0 +1,5 @@ +import frrtest + + +class TestStream(frrtest.TestRefOut): + program = "./test_stream" diff --git a/tests/lib/test_stream.refout b/tests/lib/test_stream.refout new file mode 100644 index 0000000..cf52e13 --- /dev/null +++ b/tests/lib/test_stream.refout @@ -0,0 +1,8 @@ +endp: 15, readable: 15, writeable: 1009 +0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef +endp: 15, readable: 15, writeable: 0 +0xef 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef 0xde 0xad 0xbe 0xef +c: 0xef +w: 0xbeef +l: 0xdeadbeef +q: 0xdeadbeefdeadbeef diff --git a/tests/lib/test_table.c b/tests/lib/test_table.c new file mode 100644 index 0000000..cef93ad --- /dev/null +++ b/tests/lib/test_table.c @@ -0,0 +1,509 @@ +/* + * Routing table test + * Copyright (C) 2012 OSR. + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "printfrr.h" +#include "prefix.h" +#include "table.h" + +/* + * test_node_t + * + * Information that is kept for each node in the radix tree. + */ +typedef struct test_node_t_ { + + /* + * Human readable representation of the string. Allocated using + * malloc()/dup(). + */ + char *prefix_str; +} test_node_t; + +struct thread_master *master; + +/* + * add_node + * + * Add the given prefix (passed in as a string) to the given table. + */ +static void add_node(struct route_table *table, const char *prefix_str) +{ + struct prefix_ipv4 p; + test_node_t *node; + struct route_node *rn; + + assert(prefix_str); + + if (str2prefix_ipv4(prefix_str, &p) <= 0) { + assert(0); + } + + rn = route_node_get(table, (struct prefix *)&p); + if (rn->info) { + assert(0); + return; + } + + node = malloc(sizeof(test_node_t)); + assert(node); + node->prefix_str = strdup(prefix_str); + assert(node->prefix_str); + rn->info = node; +} + +/* + * add_nodes + * + * Convenience function to add a bunch of nodes together. + * + * The arguments must be prefixes in string format, with a NULL as the + * last argument. + */ +static void add_nodes(struct route_table *table, ...) +{ + va_list arglist; + char *prefix; + + va_start(arglist, table); + + prefix = va_arg(arglist, char *); + while (prefix) { + add_node(table, prefix); + prefix = va_arg(arglist, char *); + } + + va_end(arglist); +} + +/* + * print_subtree + * + * Recursive function to print a route node and its children. + * + * @see print_table + */ +static void print_subtree(struct route_node *rn, const char *legend, + int indent_level) +{ + int i; + + /* + * Print this node first. + */ + for (i = 0; i < indent_level; i++) { + printf(" "); + } + + printfrr("%s: %pFX", legend, &rn->p); + if (!rn->info) { + printf(" (internal)"); + } + printf("\n"); + if (rn->l_left) { + print_subtree(rn->l_left, "Left", indent_level + 1); + } + if (rn->l_right) { + print_subtree(rn->l_right, "Right", indent_level + 1); + } +} + +/* + * print_table + * + * Function that prints out the internal structure of a route table. + */ +static void print_table(struct route_table *table) +{ + struct route_node *rn; + + rn = table->top; + + if (!rn) { + printf("<Empty Table>\n"); + return; + } + + print_subtree(rn, "Top", 0); +} + +/* + * clear_table + * + * Remove all nodes from the given table. + */ +static void clear_table(struct route_table *table) +{ + route_table_iter_t iter; + struct route_node *rn; + test_node_t *node; + + route_table_iter_init(&iter, table); + + while ((rn = route_table_iter_next(&iter))) { + node = rn->info; + if (!node) { + continue; + } + rn->info = NULL; + route_unlock_node(rn); + free(node->prefix_str); + free(node); + } + + route_table_iter_cleanup(&iter); + + assert(table->top == NULL); +} + +/* + * verify_next_by_iterating + * + * Iterate over the tree to make sure that the first prefix after + * target_pfx is the expected one. Note that target_pfx may not be + * present in the tree. + */ +static void verify_next_by_iterating(struct route_table *table, + struct prefix *target_pfx, + struct prefix *next_pfx) +{ + route_table_iter_t iter; + struct route_node *rn; + + route_table_iter_init(&iter, table); + while ((rn = route_table_iter_next(&iter))) { + if (route_table_prefix_iter_cmp(&rn->p, target_pfx) > 0) { + assert(!prefix_cmp(&rn->p, next_pfx)); + break; + } + } + + if (!rn) { + assert(!next_pfx); + } + + route_table_iter_cleanup(&iter); +} + +/* + * verify_next + * + * Verifies that route_table_get_next() returns the expected result + * (result) for the prefix string 'target'. + */ +static void verify_next(struct route_table *table, const char *target, + const char *next) +{ + struct prefix_ipv4 target_pfx, next_pfx; + struct route_node *rn; + char result_buf[PREFIX2STR_BUFFER]; + + if (str2prefix_ipv4(target, &target_pfx) <= 0) { + assert(0); + } + + rn = route_table_get_next(table, (struct prefix *)&target_pfx); + if (rn) { + prefix2str(&rn->p, result_buf, sizeof(result_buf)); + } else { + snprintf(result_buf, sizeof(result_buf), "(Null)"); + } + + printf("\n"); + print_table(table); + printf("Verifying successor of %s. Expected: %s, Result: %s\n", target, + next ? next : "(Null)", result_buf); + + if (!rn) { + assert(!next); + verify_next_by_iterating(table, (struct prefix *)&target_pfx, + NULL); + return; + } + + assert(next); + + if (str2prefix_ipv4(next, &next_pfx) <= 0) { + assert(0); + } + + if (prefix_cmp(&rn->p, (struct prefix *)&next_pfx)) { + assert(0); + } + route_unlock_node(rn); + + verify_next_by_iterating(table, (struct prefix *)&target_pfx, + (struct prefix *)&next_pfx); +} + +/* + * test_get_next + */ +static void test_get_next(void) +{ + struct route_table *table; + + printf("\n\nTesting route_table_get_next()\n"); + table = route_table_init(); + + /* + * Target exists in tree, but has no successor. + */ + add_nodes(table, "1.0.1.0/24", NULL); + verify_next(table, "1.0.1.0/24", NULL); + clear_table(table); + + /* + * Target exists in tree, and there is a node in its left subtree. + */ + add_nodes(table, "1.0.1.0/24", "1.0.1.0/25", NULL); + verify_next(table, "1.0.1.0/24", "1.0.1.0/25"); + clear_table(table); + + /* + * Target exists in tree, and there is a node in its right subtree. + */ + add_nodes(table, "1.0.1.0/24", "1.0.1.128/25", NULL); + verify_next(table, "1.0.1.0/24", "1.0.1.128/25"); + clear_table(table); + + /* + * Target exists in the tree, next node is outside subtree. + */ + add_nodes(table, "1.0.1.0/24", "1.1.0.0/16", NULL); + verify_next(table, "1.0.1.0/24", "1.1.0.0/16"); + clear_table(table); + + /* + * The target node does not exist in the tree for all the test cases + * below this point. + */ + + /* + * There is no successor in the tree. + */ + add_nodes(table, "1.0.0.0/16", NULL); + verify_next(table, "1.0.1.0/24", NULL); + clear_table(table); + + /* + * There exists a node that would be in the target's left subtree. + */ + add_nodes(table, "1.0.0.0/16", "1.0.1.0/25", NULL); + verify_next(table, "1.0.1.0/24", "1.0.1.0/25"); + clear_table(table); + + /* + * There exists a node would be in the target's right subtree. + */ + add_nodes(table, "1.0.0.0/16", "1.0.1.128/25", NULL); + verify_next(table, "1.0.1.0/24", "1.0.1.128/25"); + clear_table(table); + + /* + * A search for the target reaches a node where there are no child + * nodes in the direction of the target (left), but the node has a + * right child. + */ + add_nodes(table, "1.0.0.0/16", "1.0.128.0/17", NULL); + verify_next(table, "1.0.0.0/17", "1.0.128.0/17"); + clear_table(table); + + /* + * A search for the target reaches a node with no children. We have + * to go upwards in the tree to find a successor. + */ + add_nodes(table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/24", + "1.0.128.0/17", NULL); + verify_next(table, "1.0.1.0/25", "1.0.128.0/17"); + clear_table(table); + + /* + * A search for the target reaches a node where neither the node nor + * the target prefix contain each other. + * + * In first case below the node succeeds the target. + * + * In the second case, the node comes before the target, so we have + * to go up the tree looking for a successor. + */ + add_nodes(table, "1.0.0.0/16", "1.0.1.0/24", NULL); + verify_next(table, "1.0.0.0/24", "1.0.1.0/24"); + clear_table(table); + + add_nodes(table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/25", + "1.0.128.0/17", NULL); + verify_next(table, "1.0.1.128/25", "1.0.128.0/17"); + clear_table(table); + + route_table_finish(table); +} + +/* + * verify_prefix_iter_cmp + */ +static void verify_prefix_iter_cmp(const char *p1, const char *p2, + int exp_result) +{ + struct prefix_ipv4 p1_pfx, p2_pfx; + int result; + + if (str2prefix_ipv4(p1, &p1_pfx) <= 0) { + assert(0); + } + + if (str2prefix_ipv4(p2, &p2_pfx) <= 0) { + assert(0); + } + + result = route_table_prefix_iter_cmp((struct prefix *)&p1_pfx, + (struct prefix *)&p2_pfx); + + printf("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result); + + assert(exp_result == result); + + /* + * Also check the reverse comparison. + */ + result = route_table_prefix_iter_cmp((struct prefix *)&p2_pfx, + (struct prefix *)&p1_pfx); + + if (exp_result) { + exp_result = -exp_result; + } + + printf("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result); + assert(result == exp_result); +} + +/* + * test_prefix_iter_cmp + * + * Tests comparison of prefixes according to order of iteration. + */ +static void test_prefix_iter_cmp(void) +{ + printf("\n\nTesting route_table_prefix_iter_cmp()\n"); + + verify_prefix_iter_cmp("1.0.0.0/8", "1.0.0.0/8", 0); + + verify_prefix_iter_cmp("1.0.0.0/8", "1.0.0.0/16", -1); + + verify_prefix_iter_cmp("1.0.0.0/16", "1.128.0.0/16", -1); +} + +/* + * verify_iter_with_pause + * + * Iterates over a tree using two methods: 'normal' iteration, and an + * iterator that pauses at each node. Verifies that the two methods + * yield the same results. + */ +static void verify_iter_with_pause(struct route_table *table) +{ + unsigned long num_nodes; + struct route_node *rn, *iter_rn; + route_table_iter_t iter_space; + route_table_iter_t *iter = &iter_space; + + route_table_iter_init(iter, table); + num_nodes = 0; + + for (rn = route_top(table); rn; rn = route_next(rn)) { + num_nodes++; + route_table_iter_pause(iter); + + assert(iter->current == NULL); + if (route_table_iter_started(iter)) { + assert(iter->state == RT_ITER_STATE_PAUSED); + } else { + assert(rn == table->top); + assert(iter->state == RT_ITER_STATE_INIT); + } + + iter_rn = route_table_iter_next(iter); + + /* + * Make sure both iterations return the same node. + */ + assert(rn == iter_rn); + } + + assert(num_nodes == route_table_count(table)); + + route_table_iter_pause(iter); + iter_rn = route_table_iter_next(iter); + + assert(iter_rn == NULL); + assert(iter->state == RT_ITER_STATE_DONE); + + assert(route_table_iter_next(iter) == NULL); + assert(iter->state == RT_ITER_STATE_DONE); + + route_table_iter_cleanup(iter); + + print_table(table); + printf("Verified pausing iteration on tree with %lu nodes\n", + num_nodes); +} + +/* + * test_iter_pause + */ +static void test_iter_pause(void) +{ + struct route_table *table; + int i, num_prefixes; + const char *prefixes[] = {"1.0.1.0/24", "1.0.1.0/25", "1.0.1.128/25", + "1.0.2.0/24", "2.0.0.0/8"}; + + num_prefixes = array_size(prefixes); + + printf("\n\nTesting that route_table_iter_pause() works as expected\n"); + table = route_table_init(); + for (i = 0; i < num_prefixes; i++) { + add_nodes(table, prefixes[i], NULL); + } + + verify_iter_with_pause(table); + + clear_table(table); + route_table_finish(table); +} + +/* + * run_tests + */ +static void run_tests(void) +{ + test_prefix_iter_cmp(); + test_get_next(); + test_iter_pause(); +} + +/* + * main + */ +int main(void) +{ + run_tests(); +} diff --git a/tests/lib/test_table.py b/tests/lib/test_table.py new file mode 100644 index 0000000..ee1849f --- /dev/null +++ b/tests/lib/test_table.py @@ -0,0 +1,12 @@ +import frrtest + + +class TestTable(frrtest.TestMultiOut): + program = "./test_table" + + +for i in range(6): + TestTable.onesimple("Verifying cmp") +for i in range(11): + TestTable.onesimple("Verifying successor") +TestTable.onesimple("Verified pausing") diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c new file mode 100644 index 0000000..475fabd --- /dev/null +++ b/tests/lib/test_timer_correctness.c @@ -0,0 +1,186 @@ +/* + * Test program to verify that scheduled timers are executed in the + * correct order. + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include <stdio.h> +#include <unistd.h> + +#include "memory.h" +#include "prng.h" +#include "thread.h" + +#define SCHEDULE_TIMERS 800 +#define REMOVE_TIMERS 200 + +#define TIMESTR_LEN strlen("4294967296.999999") + +struct thread_master *master; + +static size_t log_buf_len; +static size_t log_buf_pos; +static char *log_buf; + +static size_t expected_buf_len; +static size_t expected_buf_pos; +static char *expected_buf; + +static struct prng *prng; + +static struct thread **timers; + +static int timers_pending; + +static void terminate_test(void) +{ + int exit_code; + + if (strcmp(log_buf, expected_buf)) { + fprintf(stderr, + "Expected output and received output differ.\n"); + fprintf(stderr, "---Expected output: ---\n%s", expected_buf); + fprintf(stderr, "---Actual output: ---\n%s", log_buf); + exit_code = 1; + } else { + printf("Expected output and actual output match.\n"); + exit_code = 0; + } + + thread_master_free(master); + XFREE(MTYPE_TMP, log_buf); + XFREE(MTYPE_TMP, expected_buf); + prng_free(prng); + XFREE(MTYPE_TMP, timers); + + exit(exit_code); +} + +static void timer_func(struct thread *thread) +{ + int rv; + + rv = snprintf(log_buf + log_buf_pos, log_buf_len - log_buf_pos, "%s\n", + (char *)thread->arg); + assert(rv >= 0); + log_buf_pos += rv; + assert(log_buf_pos < log_buf_len); + XFREE(MTYPE_TMP, thread->arg); + + timers_pending--; + if (!timers_pending) + terminate_test(); +} + +static int cmp_timeval(const void *a, const void *b) +{ + const struct timeval *ta = *(struct timeval * const *)a; + const struct timeval *tb = *(struct timeval * const *)b; + + if (timercmp(ta, tb, <)) + return -1; + if (timercmp(ta, tb, >)) + return 1; + return 0; +} + +int main(int argc, char **argv) +{ + int i, j; + struct thread t; + struct timeval **alarms; + + master = thread_master_create(NULL); + + log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; + log_buf_pos = 0; + log_buf = XMALLOC(MTYPE_TMP, log_buf_len); + + expected_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; + expected_buf_pos = 0; + expected_buf = XMALLOC(MTYPE_TMP, expected_buf_len); + + prng = prng_new(0); + + timers = XCALLOC(MTYPE_TMP, SCHEDULE_TIMERS * sizeof(*timers)); + + for (i = 0; i < SCHEDULE_TIMERS; i++) { + long interval_msec; + int ret; + char *arg; + + /* Schedule timers to expire in 0..5 seconds */ + interval_msec = prng_rand(prng) % 5000; + arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1); + thread_add_timer_msec(master, timer_func, arg, interval_msec, + &timers[i]); + ret = snprintf(arg, TIMESTR_LEN + 1, "%lld.%06lld", + (long long)timers[i]->u.sands.tv_sec, + (long long)timers[i]->u.sands.tv_usec); + assert(ret > 0); + assert((size_t)ret < TIMESTR_LEN + 1); + timers_pending++; + } + + for (i = 0; i < REMOVE_TIMERS; i++) { + int index; + + index = prng_rand(prng) % SCHEDULE_TIMERS; + if (!timers[index]) + continue; + + XFREE(MTYPE_TMP, timers[index]->arg); + thread_cancel(&timers[index]); + timers_pending--; + } + + /* We create an array of pointers to the alarm times and sort + * that array. That sorted array is used to generate a string + * representing the expected "output" of the timers when they + * are run. */ + j = 0; + alarms = XCALLOC(MTYPE_TMP, timers_pending * sizeof(*alarms)); + for (i = 0; i < SCHEDULE_TIMERS; i++) { + if (!timers[i]) + continue; + alarms[j++] = &timers[i]->u.sands; + } + qsort(alarms, j, sizeof(*alarms), cmp_timeval); + for (i = 0; i < j; i++) { + int ret; + + ret = snprintf(expected_buf + expected_buf_pos, + expected_buf_len - expected_buf_pos, + "%lld.%06lld\n", (long long)alarms[i]->tv_sec, + (long long)alarms[i]->tv_usec); + assert(ret > 0); + expected_buf_pos += ret; + assert(expected_buf_pos < expected_buf_len); + } + XFREE(MTYPE_TMP, alarms); + + while (thread_fetch(master, &t)) + thread_call(&t); + + return 0; +} diff --git a/tests/lib/test_timer_correctness.py b/tests/lib/test_timer_correctness.py new file mode 100644 index 0000000..71f45f9 --- /dev/null +++ b/tests/lib/test_timer_correctness.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestTimerCorrectness(frrtest.TestMultiOut): + program = "./test_timer_correctness" + + +TestTimerCorrectness.onesimple("Expected output and actual output match.") diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c new file mode 100644 index 0000000..c1dd07b --- /dev/null +++ b/tests/lib/test_timer_performance.c @@ -0,0 +1,99 @@ +/* + * Test program which measures the time it takes to schedule and + * remove timers. + * + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of Quagga + * + * Quagga is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * Quagga is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include <stdio.h> +#include <unistd.h> + +#include "thread.h" +#include "prng.h" + +#define SCHEDULE_TIMERS 1000000 +#define REMOVE_TIMERS 500000 + +struct thread_master *master; + +static void dummy_func(struct thread *thread) +{ +} + +int main(int argc, char **argv) +{ + struct prng *prng; + int i; + struct thread **timers; + struct timeval tv_start, tv_lap, tv_stop; + unsigned long t_schedule, t_remove; + + master = thread_master_create(NULL); + prng = prng_new(0); + timers = calloc(SCHEDULE_TIMERS, sizeof(*timers)); + + /* create thread structures so they won't be allocated during the + * time measurement */ + for (i = 0; i < SCHEDULE_TIMERS; i++) { + thread_add_timer_msec(master, dummy_func, NULL, 0, &timers[i]); + } + for (i = 0; i < SCHEDULE_TIMERS; i++) + thread_cancel(&timers[i]); + + monotime(&tv_start); + + for (i = 0; i < SCHEDULE_TIMERS; i++) { + long interval_msec; + + interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS); + thread_add_timer_msec(master, dummy_func, NULL, interval_msec, + &timers[i]); + } + + monotime(&tv_lap); + + for (i = 0; i < REMOVE_TIMERS; i++) { + int index; + + index = prng_rand(prng) % SCHEDULE_TIMERS; + thread_cancel(&timers[index]); + } + + monotime(&tv_stop); + + t_schedule = 1000 * (tv_lap.tv_sec - tv_start.tv_sec); + t_schedule += (tv_lap.tv_usec - tv_start.tv_usec) / 1000; + + t_remove = 1000 * (tv_stop.tv_sec - tv_lap.tv_sec); + t_remove += (tv_stop.tv_usec - tv_lap.tv_usec) / 1000; + + printf("Scheduling %d random timers took %lu.%03lu seconds.\n", + SCHEDULE_TIMERS, t_schedule / 1000, t_schedule % 1000); + printf("Removing %d random timers took %lu.%03lu seconds.\n", + REMOVE_TIMERS, t_remove / 1000, t_remove % 1000); + fflush(stdout); + + free(timers); + thread_master_free(master); + prng_free(prng); + return 0; +} diff --git a/tests/lib/test_ttable.c b/tests/lib/test_ttable.c new file mode 100644 index 0000000..43b8adc --- /dev/null +++ b/tests/lib/test_ttable.c @@ -0,0 +1,182 @@ +/* + * ASCII table generator. + * Copyright (C) 2017 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <termtable.h> +#include <memory.h> + +int main(int argc, char **argv) +{ + char *table; + + struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_ASCII]); + + /* test printf compatibility and dimension counters */ + ttable_add_row(tt, "%s|%s|%s", "Column 1", "Column 2", "Column 3"); + assert(tt->ncols == 3); + assert(tt->nrows == 1); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* add new row with 1 column, assert that it is not added */ + assert(ttable_add_row(tt, "%s", "Garbage") == NULL); + assert(tt->ncols == 3); + assert(tt->nrows == 1); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* add new row, assert that it is added */ + assert(ttable_add_row(tt, "%s|%s|%s", "a", "b", "c")); + assert(tt->ncols == 3); + assert(tt->nrows == 2); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* add empty row, assert that it is added */ + assert(ttable_add_row(tt, "||")); + assert(tt->ncols == 3); + assert(tt->nrows == 3); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* delete 1st row, assert that it is removed */ + ttable_del_row(tt, 0); + assert(tt->ncols == 3); + assert(tt->nrows == 2); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* delete last row, assert that it is removed */ + ttable_del_row(tt, 0); + assert(tt->ncols == 3); + assert(tt->nrows == 1); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* delete the remaining row, check dumping an empty table */ + ttable_del_row(tt, 0); + assert(tt->ncols == 0); + assert(tt->nrows == 0); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* add new row */ + ttable_add_row(tt, "%s|%s||%s|%9d", "slick", "black", "triple", 1337); + assert(tt->ncols == 5); + assert(tt->nrows == 1); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* add bigger row */ + ttable_add_row(tt, "%s|%s||%s|%s", + "nebula dusk session streets twilight pioneer beats yeah", + "prarie dog", "cornmeal", ":O -*_-*"); + assert(tt->ncols == 5); + assert(tt->nrows == 2); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* insert new row at beginning */ + ttable_insert_row(tt, 0, "%s|%s||%d|%lf", "converting", "vegetarians", + 2, 2015.0); + assert(tt->ncols == 5); + assert(tt->nrows == 3); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* insert new row at end */ + ttable_insert_row(tt, tt->nrows - 1, "%s|%s||%d|%ld", "converting", + "vegetarians", 1, 2003L); + assert(tt->ncols == 5); + assert(tt->nrows == 4); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* insert new row at middle */ + ttable_insert_row(tt, 1, "%s|%s||%s|%ld", "she", "pioneer", "aki", 1l); + assert(tt->ncols == 5); + assert(tt->nrows == 5); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* set alignment */ + ttable_align(tt, 0, 1, 2, 2, LEFT); + assert(tt->ncols == 5); + assert(tt->nrows == 5); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + ttable_align(tt, 0, 1, 5, 1, RIGHT); + assert(tt->ncols == 5); + assert(tt->nrows == 5); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* set padding */ + ttable_pad(tt, 0, 1, 1, 1, RIGHT, 2); + assert(tt->ncols == 5); + assert(tt->nrows == 5); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + ttable_pad(tt, 0, 0, 5, 4, LEFT, 2); + assert(tt->ncols == 5); + assert(tt->nrows == 5); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* restyle */ + tt->style.cell.border.bottom_on = false; + tt->style.cell.border.top_on = false; + tt->style.cell.border.right_on = false; + tt->style.cell.border.left_on = false; + ttable_restyle(tt); + + /* top & double bottom border for top row */ + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + ttable_rowseps(tt, 1, TOP, true, '-'); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* column separators for leftmost column */ + ttable_colseps(tt, 0, RIGHT, true, '|'); + table = ttable_dump(tt, "\n"); + fprintf(stdout, "%s\n", table); + XFREE(MTYPE_TMP, table); + + /* delete table */ + ttable_del(tt); +} diff --git a/tests/lib/test_ttable.py b/tests/lib/test_ttable.py new file mode 100644 index 0000000..9151181 --- /dev/null +++ b/tests/lib/test_ttable.py @@ -0,0 +1,5 @@ +import frrtest + + +class TestTTable(frrtest.TestRefOut): + program = "./test_ttable" diff --git a/tests/lib/test_ttable.refout b/tests/lib/test_ttable.refout new file mode 100644 index 0000000..fb59c0f --- /dev/null +++ b/tests/lib/test_ttable.refout @@ -0,0 +1,143 @@ + |--------------------------------| + | Column 1 | Column 2 | Column 3 | + |--------------------------------| + + |--------------------------------| + | Column 1 | Column 2 | Column 3 | + |--------------------------------| + + |--------------------------------| + | Column 1 | Column 2 | Column 3 | + |----------+----------+----------| + | a | b | c | + |--------------------------------| + + |--------------------------------| + | Column 1 | Column 2 | Column 3 | + |----------+----------+----------| + | a | b | c | + |----------+----------+----------| + | | | | + |--------------------------------| + + |-----------| + | a | b | c | + |---+---+---| + | | | | + |-----------| + + |--------| + | | | | + |--------| + + || + || + + |---------------------------------------| + | slick | black | | triple | 1337 | + |---------------------------------------| + + |------------------------------------------------------------------------------------------------| + | slick | black | | triple | 1337 | + |---------------------------------------------------------+------------+--+----------+-----------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |------------------------------------------------------------------------------------------------| + + |---------------------------------------------------------------------------------------------------| + | converting | vegetarians | | 2 | 2015.000000 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | slick | black | | triple | 1337 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |---------------------------------------------------------------------------------------------------| + + |---------------------------------------------------------------------------------------------------| + | converting | vegetarians | | 2 | 2015.000000 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | slick | black | | triple | 1337 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | converting | vegetarians | | 1 | 2003 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |---------------------------------------------------------------------------------------------------| + + |---------------------------------------------------------------------------------------------------| + | converting | vegetarians | | 2 | 2015.000000 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | she | pioneer | | aki | 1 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | slick | black | | triple | 1337 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | converting | vegetarians | | 1 | 2003 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |---------------------------------------------------------------------------------------------------| + + |---------------------------------------------------------------------------------------------------| + | converting | vegetarians | | 2 | 2015.000000 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | she | pioneer | | aki | 1 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | slick | black | | triple | 1337 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | converting | vegetarians | | 1 | 2003 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |---------------------------------------------------------------------------------------------------| + + |---------------------------------------------------------------------------------------------------| + | converting | vegetarians | | 2 | 2015.000000 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | she | pioneer | | aki | 1 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | slick | black | | triple | 1337 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | converting | vegetarians | | 1 | 2003 | + |---------------------------------------------------------+-------------+--+----------+-------------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |---------------------------------------------------------------------------------------------------| + + |----------------------------------------------------------------------------------------------------| + | converting | vegetarians | | 2 | 2015.000000 | + |---------------------------------------------------------+--------------+--+----------+-------------| + | she | pioneer | | aki | 1 | + |---------------------------------------------------------+--------------+--+----------+-------------| + | slick | black | | triple | 1337 | + |---------------------------------------------------------+--------------+--+----------+-------------| + | converting | vegetarians | | 1 | 2003 | + |---------------------------------------------------------+--------------+--+----------+-------------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |----------------------------------------------------------------------------------------------------| + + |--------------------------------------------------------------------------------------------------------| + | converting | vegetarians | | 2 | 2015.000000 | + |----------------------------------------------------------+---------------+---+-----------+-------------| + | she | pioneer | | aki | 1 | + |----------------------------------------------------------+---------------+---+-----------+-------------| + | slick | black | | triple | 1337 | + |----------------------------------------------------------+---------------+---+-----------+-------------| + | converting | vegetarians | | 1 | 2003 | + |----------------------------------------------------------+---------------+---+-----------+-------------| + | nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* | + |--------------------------------------------------------------------------------------------------------| + + |-----------------------------------------------------------------------------------------------| + | converting vegetarians 2 2015.000000 | + |-----------------------------------------------------------------------------------------------| + |-----------------------------------------------------------------------------------------------| + | she pioneer aki 1 | + | slick black triple 1337 | + | converting vegetarians 1 2003 | + | nebula dusk session streets twilight pioneer beats yeah prarie dog cornmeal :O -*_-* | + |-----------------------------------------------------------------------------------------------| + + |------------------------------------------------------------------------------------------------| + | converting | vegetarians 2 2015.000000 | + |---------------------------------------------------------+--------------------------------------| + |---------------------------------------------------------+--------------------------------------| + | she | pioneer aki 1 | + | slick | black triple 1337 | + | converting | vegetarians 1 2003 | + | nebula dusk session streets twilight pioneer beats yeah | prarie dog cornmeal :O -*_-* | + |------------------------------------------------------------------------------------------------| + diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c new file mode 100644 index 0000000..6e69658 --- /dev/null +++ b/tests/lib/test_typelist.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include <arpa/inet.h> + +#define WNO_ATOMLIST_UNSAFE_FIND + +#include "typesafe.h" +#include "atomlist.h" +#include "memory.h" +#include "monotime.h" +#include "jhash.h" +#include "sha256.h" +#include "printfrr.h" + +#include "tests/helpers/c/prng.h" + +/* note: these macros are layered 2-deep because that makes the C + * preprocessor expand the "type" argument. Otherwise, you get + * "PREDECL_type" instead of "PREDECL_LIST" + */ +#define _concat(a, b) a ## b +#define concat(a, b) _concat(a, b) +#define _str(x) #x +#define str(x) _str(x) + +#define _PREDECL(type, ...) PREDECL_##type(__VA_ARGS__) +#define PREDECL(type, ...) _PREDECL(type, __VA_ARGS__) +#define _DECLARE(type, ...) DECLARE_##type(__VA_ARGS__) +#define DECLARE(type, ...) _DECLARE(type, __VA_ARGS__) + +#define T_SORTED (1 << 0) +#define T_UNIQ (1 << 1) +#define T_HASH (1 << 2) +#define T_HEAP (1 << 3) +#define T_ATOMIC (1 << 4) +#define T_REVERSE (1 << 5) + +#define _T_LIST (0) +#define _T_DLIST (0 | T_REVERSE) +#define _T_ATOMLIST (0 | T_ATOMIC) +#define _T_HEAP (T_SORTED | T_HEAP) +#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ) +#define _T_SORTLIST_NONUNIQ (T_SORTED) +#define _T_HASH (T_SORTED | T_UNIQ | T_HASH) +#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ) +#define _T_SKIPLIST_NONUNIQ (T_SORTED) +#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ | T_REVERSE) +#define _T_RBTREE_NONUNIQ (T_SORTED | T_REVERSE) +#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC) +#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC) + +#define _T_TYPE(type) _T_##type +#define IS_SORTED(type) (_T_TYPE(type) & T_SORTED) +#define IS_UNIQ(type) (_T_TYPE(type) & T_UNIQ) +#define IS_HASH(type) (_T_TYPE(type) & T_HASH) +#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP) +#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC) +#define IS_REVERSE(type) (_T_TYPE(type) & T_REVERSE) + +static struct timeval ref, ref0; + +static void ts_start(void) +{ + monotime(&ref0); + monotime(&ref); +} +static void ts_ref(const char *text) +{ + int64_t us; + us = monotime_since(&ref, NULL); + printfrr("%7"PRId64"us %s\n", us, text); + monotime(&ref); +} +static void ts_end(void) +{ + int64_t us; + us = monotime_since(&ref0, NULL); + printfrr("%7"PRId64"us total\n", us); +} + +#define TYPE LIST +#include "test_typelist.h" + +#define TYPE DLIST +#include "test_typelist.h" + +#define TYPE ATOMLIST +#include "test_typelist.h" + +#define TYPE HEAP +#include "test_typelist.h" + +#define TYPE SORTLIST_UNIQ +#include "test_typelist.h" + +#define TYPE SORTLIST_NONUNIQ +#include "test_typelist.h" + +#define TYPE HASH +#include "test_typelist.h" + +#define TYPE HASH_collisions +#define REALTYPE HASH +#define SHITTY_HASH +#include "test_typelist.h" +#undef SHITTY_HASH + +#define TYPE SKIPLIST_UNIQ +#include "test_typelist.h" + +#define TYPE SKIPLIST_NONUNIQ +#include "test_typelist.h" + +#define TYPE RBTREE_UNIQ +#include "test_typelist.h" + +#define TYPE RBTREE_NONUNIQ +#include "test_typelist.h" + +#define TYPE ATOMSORT_UNIQ +#include "test_typelist.h" + +#define TYPE ATOMSORT_NONUNIQ +#include "test_typelist.h" + +int main(int argc, char **argv) +{ + srandom(1); + + test_LIST(); + test_DLIST(); + test_ATOMLIST(); + test_HEAP(); + test_SORTLIST_UNIQ(); + test_SORTLIST_NONUNIQ(); + test_HASH(); + test_HASH_collisions(); + test_SKIPLIST_UNIQ(); + test_SKIPLIST_NONUNIQ(); + test_RBTREE_UNIQ(); + test_RBTREE_NONUNIQ(); + test_ATOMSORT_UNIQ(); + test_ATOMSORT_NONUNIQ(); + + log_memstats_stderr("test: "); + return 0; +} diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h new file mode 100644 index 0000000..e3579c6 --- /dev/null +++ b/tests/lib/test_typelist.h @@ -0,0 +1,828 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* C++ called, they want their templates back */ +#define item concat(item_, TYPE) +#define itm concat(itm_, TYPE) +#define itmswap concat(itmswap_, TYPE) +#define head concat(head_, TYPE) +#define list concat(TYPE, ) +#define list_head concat(TYPE, _head) +#define list_item concat(TYPE, _item) +#define list_cmp concat(TYPE, _cmp) +#define list_hash concat(TYPE, _hash) +#define list_init concat(TYPE, _init) +#define list_fini concat(TYPE, _fini) +#define list_const_first concat(TYPE, _const_first) +#define list_first concat(TYPE, _first) +#define list_const_next concat(TYPE, _const_next) +#define list_next concat(TYPE, _next) +#define list_next_safe concat(TYPE, _next_safe) +#define list_const_last concat(TYPE, _const_last) +#define list_last concat(TYPE, _last) +#define list_const_prev concat(TYPE, _const_prev) +#define list_prev concat(TYPE, _prev) +#define list_prev_safe concat(TYPE, _prev_safe) +#define list_count concat(TYPE, _count) +#define list_add concat(TYPE, _add) +#define list_add_head concat(TYPE, _add_head) +#define list_add_tail concat(TYPE, _add_tail) +#define list_add_after concat(TYPE, _add_after) +#define list_find concat(TYPE, _find) +#define list_find_lt concat(TYPE, _find_lt) +#define list_find_gteq concat(TYPE, _find_gteq) +#define list_member concat(TYPE, _member) +#define list_anywhere concat(TYPE, _anywhere) +#define list_del concat(TYPE, _del) +#define list_pop concat(TYPE, _pop) +#define list_swap_all concat(TYPE, _swap_all) + +#define ts_hash_head concat(ts_hash_head_, TYPE) + +#ifndef REALTYPE +#define REALTYPE TYPE +#endif + +PREDECL(REALTYPE, list); +struct item { + uint64_t val; + struct list_item itm; + int scratchpad; +}; + +#if IS_SORTED(REALTYPE) +static int list_cmp(const struct item *a, const struct item *b); + +#if IS_HASH(REALTYPE) +static uint32_t list_hash(const struct item *a); +DECLARE(REALTYPE, list, struct item, itm, list_cmp, list_hash); + +static uint32_t list_hash(const struct item *a) +{ +#ifdef SHITTY_HASH + /* crappy hash to get some hash collisions */ + return a->val ^ (a->val << 29) ^ 0x55AA0000U; +#else + return jhash_1word(a->val, 0xdeadbeef); +#endif +} + +#else +DECLARE(REALTYPE, list, struct item, itm, list_cmp); +#endif + +static int list_cmp(const struct item *a, const struct item *b) +{ + if (a->val > b->val) + return 1; + if (a->val < b->val) + return -1; + return 0; +} + +#else /* !IS_SORTED */ +DECLARE(REALTYPE, list, struct item, itm); +#endif + +#define NITEM 10000 +#define NITEM_SWAP 100 /* other container for swap */ +struct item itm[NITEM], itmswap[NITEM_SWAP]; +static struct list_head head = concat(INIT_, REALTYPE)(head); + +static void ts_hash_head(struct list_head *h, const char *text, + const char *expect) +{ + int64_t us = monotime_since(&ref, NULL); + SHA256_CTX ctx; + struct item *item; + unsigned i = 0; + uint8_t hash[32]; + char hashtext[65]; + uint32_t swap_count, count; + + count = list_count(h); + swap_count = htonl(count); + + SHA256_Init(&ctx); + SHA256_Update(&ctx, &swap_count, sizeof(swap_count)); + + frr_each (list, h, item) { + struct { + uint32_t val_upper, val_lower, index; + } hashitem = { + htonl(item->val >> 32), + htonl(item->val & 0xFFFFFFFFULL), + htonl(i), + }; + SHA256_Update(&ctx, &hashitem, sizeof(hashitem)); + i++; + assert(i <= count); + } + SHA256_Final(hash, &ctx); + + for (i = 0; i < sizeof(hash); i++) + sprintf(hashtext + i * 2, "%02x", hash[i]); + + printfrr("%7"PRId64"us %-25s %s%s\n", us, text, + expect ? " " : "*", hashtext); + if (expect && strcmp(expect, hashtext)) { + printfrr("%-21s %s\n", "EXPECTED:", expect); + assert(0); + } + monotime(&ref); +} +/* hashes will have different item ordering */ +#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE) +#define ts_hash(pos, csum) ts_hash_head(&head, pos, NULL) +#define ts_hashx(pos, csum) ts_hash_head(&head, pos, NULL) +#define ts_hash_headx(head, pos, csum) ts_hash_head(head, pos, NULL) +#else +#define ts_hash(pos, csum) ts_hash_head(&head, pos, csum) +#define ts_hashx(pos, csum) ts_hash_head(&head, pos, csum) +#define ts_hash_headx(head, pos, csum) ts_hash_head(head, pos, csum) +#endif + +static void concat(test_, TYPE)(void) +{ + size_t i, j, k, l; + struct prng *prng; + struct prng *prng_swap __attribute__((unused)); + struct item *item, *prev __attribute__((unused)); + struct item dummy __attribute__((unused)); + + memset(itm, 0, sizeof(itm)); + for (i = 0; i < NITEM; i++) + itm[i].val = i; + + memset(itmswap, 0, sizeof(itmswap)); + for (i = 0; i < NITEM_SWAP; i++) + itmswap[i].val = i; + + printfrr("%s start\n", str(TYPE)); + ts_start(); + + list_init(&head); + assert(list_first(&head) == NULL); +#if IS_REVERSE(REALTYPE) + assert(list_last(&head) == NULL); +#endif + + ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + +#if IS_SORTED(REALTYPE) + prng = prng_new(0); + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } +#if !IS_HEAP(REALTYPE) + else + assert(list_add(&head, &itm[j]) == &itm[j]); +#endif + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hashx("fill", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); + +#if !IS_ATOMIC(REALTYPE) + struct list_head other; + + list_init(&other); + list_swap_all(&head, &other); + + assert(list_count(&head) == 0); + assert(!list_first(&head)); + assert(list_count(&other) == k); + assert(list_first(&other) != NULL); +#if IS_REVERSE(REALTYPE) + assert(!list_last(&head)); + assert(list_last(&other) != NULL); +#endif + ts_hash_headx( + &other, "swap1", + "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); + + prng_swap = prng_new(0x1234dead); + l = 0; + for (i = 0; i < NITEM_SWAP; i++) { + j = prng_rand(prng_swap) % NITEM_SWAP; + if (itmswap[j].scratchpad == 0) { + list_add(&head, &itmswap[j]); + itmswap[j].scratchpad = 1; + l++; + } +#if !IS_HEAP(REALTYPE) + else { + struct item *rv = list_add(&head, &itmswap[j]); + assert(rv == &itmswap[j]); + } +#endif + } + assert(list_count(&head) == l); + assert(list_first(&head) != NULL); + ts_hash_headx( + &head, "swap-fill", + "26df437174051cf305d1bbb62d779ee450ca764167a1e7a94be1aece420008e6"); + + list_swap_all(&head, &other); + + assert(list_count(&other) == l); + assert(list_first(&other)); + ts_hash_headx( + &other, "swap2a", + "26df437174051cf305d1bbb62d779ee450ca764167a1e7a94be1aece420008e6"); + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash_headx( + &head, "swap2b", + "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); + + while (list_pop(&other)) + ; + list_fini(&other); + prng_free(prng_swap); + + ts_ref("swap-cleanup"); +#endif /* !IS_ATOMIC */ + + k = 0; + +#if IS_ATOMIC(REALTYPE) + struct list_head *chead = &head; + struct item *citem, *cprev = NULL; + + frr_each(list, chead, citem) { +#else + const struct list_head *chead = &head; + const struct item *citem, *cprev = NULL; + + frr_each(list_const, chead, citem) { +#endif + +#if IS_HASH(REALTYPE) || IS_HEAP(REALTYPE) + /* hash table doesn't give sorting */ + (void)cprev; +#else + assert(!cprev || cprev->val < citem->val); +#if IS_REVERSE(REALTYPE) + assert(list_const_prev(chead, citem) == cprev); +#endif +#endif + cprev = citem; + k++; + } + assert(list_count(chead) == k); +#if IS_REVERSE(REALTYPE) + assert(cprev == list_const_last(chead)); +#endif + ts_ref("walk"); + +#if IS_REVERSE(REALTYPE) && !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE) + cprev = NULL; + k = 0; + + frr_rev_each(list_const, chead, citem) { + assert(!cprev || cprev->val > citem->val); + assert(list_const_next(chead, citem) == cprev); + + cprev = citem; + k++; + } + assert(list_count(chead) == k); + assert(cprev == list_const_first(chead)); + + ts_ref("reverse-walk"); +#endif + +#if IS_UNIQ(REALTYPE) + prng_free(prng); + prng = prng_new(0); + + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + dummy.val = j; + assert(list_find(&head, &dummy) == &itm[j]); + } + ts_ref("find"); + + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + memset(&dummy, 0, sizeof(dummy)); + dummy.val = j; + if (itm[j].scratchpad) + assert(list_add(&head, &dummy) == &itm[j]); + else { + assert(list_add(&head, &dummy) == NULL); + assert(list_del(&head, &dummy) != NULL); + } + } + ts_hashx("add-dup", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); + +#elif IS_HEAP(REALTYPE) + /* heap - partially sorted. */ + prev = NULL; + l = k / 4; + for (i = 0; i < l; i++) { + item = list_pop(&head); + if (prev) + assert(prev->val < item->val); + item->scratchpad = 0; + k--; + prev = item; + } + ts_hash("pop#1", NULL); + + for (i = 0; i < NITEM; i++) + assertf(list_member(&head, &itm[i]) == itm[i].scratchpad, + "%zu should:%d is:%d", i, itm[i].scratchpad, + list_member(&head, &itm[i])); + ts_hash("member", NULL); + + l = k / 2; + for (; i < l; i++) { + item = list_pop(&head); + if (prev) + assert(prev->val < item->val); + item->scratchpad = 0; + k--; + prev = item; + } + ts_hash("pop#2", NULL); + +#else /* !IS_UNIQ(REALTYPE) && !IS_HEAP(REALTYPE) */ + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + memset(&dummy, 0, sizeof(dummy)); + dummy.val = j; + + list_add(&head, &dummy); + if (itm[j].scratchpad) { + struct item *lt, *gteq, dummy2; + + assert(list_next(&head, &itm[j]) == &dummy || + list_next(&head, &dummy) == &itm[j]); + + memset(&dummy2, 0, sizeof(dummy)); + dummy2.val = j; + lt = list_find_lt(&head, &dummy2); + gteq = list_find_gteq(&head, &dummy2); + + assert(gteq == &itm[j] || gteq == &dummy); + if (lt) + assert(list_next(&head, lt) == &itm[j] || + list_next(&head, lt) == &dummy); + else + assert(list_first(&head) == &itm[j] || + list_first(&head) == &dummy); + } else if (list_next(&head, &dummy)) + assert(list_next(&head, &dummy)->val > j); + assert(list_del(&head, &dummy) != NULL); + } + ts_hash("add-dup+find_{lt,gteq}", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); +#endif +#if !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE) + prng_free(prng); + prng = prng_new(123456); + + l = 0; + for (i = 0; i < NITEM; i++) { + struct item *lt, *gteq, *tmp; + + j = prng_rand(prng) % NITEM; + dummy.val = j; + + lt = list_find_lt(&head, &dummy); + gteq = list_find_gteq(&head, &dummy); + + if (lt) { + assert(lt->val < j); + tmp = list_next(&head, lt); + assert(tmp == gteq); + assert(!tmp || tmp->val >= j); + } else + assert(gteq == list_first(&head)); + + if (gteq) + assert(gteq->val >= j); + } + ts_ref("find_{lt,gteq}"); +#endif /* !IS_HASH */ + + prng_free(prng); + prng = prng_new(0); + + l = 0; + for (i = 0; i < NITEM; i++) { + (void)prng_rand(prng); + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + assert(list_del(&head, &itm[j]) != NULL); + itm[j].scratchpad = 0; + l++; + } + } + assert(l + list_count(&head) == k); + ts_hashx("del", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca"); + +#if !IS_ATOMIC(REALTYPE) + for (i = 0; i < NITEM; i++) + assertf(list_member(&head, &itm[i]) == itm[i].scratchpad, + "%zu should:%d is:%d", i, itm[i].scratchpad, + list_member(&head, &itm[i])); + ts_hashx("member", "cb2e5d80f08a803ef7b56c15e981b681adcea214bebc2f55e12e0bfb242b07ca"); +#endif + + frr_each_safe(list, &head, item) { + assert(item->scratchpad != 0); + + if (item->val & 1) { + assert(list_del(&head, item) != NULL); + item->scratchpad = 0; + l++; + } + } + assert(l + list_count(&head) == k); + ts_hashx("frr_each_safe+del", "e0beb71dd963a75af05b722b8e71b61b304587d860c8accdc4349067542b86bb"); + +#else /* !IS_SORTED */ + prng = prng_new(0); + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add_tail(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); +#if IS_REVERSE(REALTYPE) + assert(list_last(&head) != NULL); +#endif + ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19"); + +#if !IS_ATOMIC(REALTYPE) + struct list_head other; + + list_init(&other); + list_swap_all(&head, &other); + + assert(list_count(&head) == 0); + assert(!list_first(&head)); + assert(list_count(&other) == k); + assert(list_first(&other) != NULL); +#if IS_REVERSE(REALTYPE) + assert(!list_last(&head)); + assert(list_last(&other) != NULL); +#endif + ts_hash_head( + &other, "swap1", + "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19"); + + prng_swap = prng_new(0x1234dead); + l = 0; + for (i = 0; i < NITEM_SWAP; i++) { + j = prng_rand(prng_swap) % NITEM_SWAP; + if (itmswap[j].scratchpad == 0) { + list_add_tail(&head, &itmswap[j]); + itmswap[j].scratchpad = 1; + l++; + } + } + assert(list_count(&head) == l); + assert(list_first(&head) != NULL); + ts_hash_head( + &head, "swap-fill", + "833e6ae437e322dfbd36eda8cfc33a61109be735b43f15d256c05e52d1b01909"); + + list_swap_all(&head, &other); + + assert(list_count(&other) == l); + assert(list_first(&other)); + ts_hash_head( + &other, "swap2a", + "833e6ae437e322dfbd36eda8cfc33a61109be735b43f15d256c05e52d1b01909"); + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash_head( + &head, "swap2b", + "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19"); + + while (list_pop(&other)) + ; + list_fini(&other); + prng_free(prng_swap); + + ts_ref("swap-cleanup"); +#endif + + for (i = 0; i < NITEM / 2; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + assert(list_del(&head, &itm[j]) != NULL); + itm[j].scratchpad = 0; + k--; + } + } + ts_hash("del-prng", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564"); + +#if !IS_ATOMIC(REALTYPE) + for (i = 0; i < NITEM; i++) { + assertf(list_member(&head, &itm[i]) == itm[i].scratchpad, + "%zu should:%d is:%d", i, itm[i].scratchpad, + list_member(&head, &itm[i])); + assertf(list_anywhere(&itm[i]) == itm[i].scratchpad, + "%zu should:%d is:%d", i, itm[i].scratchpad, + list_anywhere(&itm[i])); + } + ts_hash("member", "86d568a95eb429dab3162976c5a5f3f75aabc835932cd682aa280b6923549564"); +#endif + + l = 0; + while (l < (k / 4) && (prev = list_pop(&head))) { + assert(prev->scratchpad != 0); + + prev->scratchpad = 0; + l++; + } + ts_hash("pop#1", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d"); + +#if !IS_ATOMIC(REALTYPE) + for (i = 0; i < NITEM; i++) { + assertf(list_member(&head, &itm[i]) == itm[i].scratchpad, + "%zu should:%d is:%d", i, itm[i].scratchpad, + list_member(&head, &itm[i])); + assertf(list_anywhere(&itm[i]) == itm[i].scratchpad, + "%zu should:%d is:%d", i, itm[i].scratchpad, + list_anywhere(&itm[i])); + } + ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d"); +#endif +#if IS_REVERSE(REALTYPE) + i = 0; + prev = NULL; + + frr_rev_each (list, &head, item) { + assert(item->scratchpad != 0); + assert(list_next(&head, item) == prev); + + i++; + prev = item; + } + assert(list_first(&head) == prev); + assert(list_count(&head) == i); + ts_hash("reverse-walk", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d"); +#endif + + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop#2", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + prng_free(prng); + prng = prng_new(0x1e5a2d69); + + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + list_add_head(&head, &itm[j]); + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_head", "3084d8f8a28b8c756ccc0a92d60d86f6d776273734ddc3f9e1d89526f5ca2795"); + + for (i = 0; i < NITEM / 2; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 1) { + assert(list_del(&head, &itm[j]) != NULL); + itm[j].scratchpad = 0; + k--; + } + } + ts_hash("del-prng", "dc916fa7ea4418792c7c8232d74df2887f9975ead4222f4b977be6bc0b52285e"); + + l = 0; + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + prng_free(prng); + prng = prng_new(0x692d1e5a); + + k = 0; + for (i = 0; i < NITEM; i++) { + j = prng_rand(prng) % NITEM; + if (itm[j].scratchpad == 0) { + if (prng_rand(prng) & 1) { + list_add_tail(&head, &itm[j]); + } else { + list_add_head(&head, &itm[j]); + } + itm[j].scratchpad = 1; + k++; + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("fill / add_{head,tail}", "93fa180a575c96e4b6c3775c2de7843ee3254dd6ed5af699bbe155f994114b06"); + + for (i = 0; i < NITEM * 3; i++) { + int op = prng_rand(prng); + j = prng_rand(prng) % NITEM; + + if (op & 1) { + /* delete or pop */ + if (op & 2) { + item = list_pop(&head); + if (!item) + continue; + } else { + item = &itm[j]; + if (item->scratchpad == 0) + continue; + assert(list_del(&head, item) != NULL); + } + item->scratchpad = 0; + k--; + } else { + item = &itm[j]; + if (item->scratchpad != 0) + continue; + + item->scratchpad = 1; + k++; + + switch ((op >> 1) & 1) { + case 0: + list_add_head(&head, item); + break; + case 1: + list_add_tail(&head, item); + break; + default: + assert(0); + } + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("prng add/del", "4909f31d06bb006efca4dfeebddb8de071733ddf502f89b6d532155208bbc6df"); + +#if !IS_ATOMIC(REALTYPE) + /* variant with add_after */ + + for (i = 0; i < NITEM * 3; i++) { + int op = prng_rand(prng); + j = prng_rand(prng) % NITEM; + + if (op & 1) { + /* delete or pop */ + if (op & 2) { + item = list_pop(&head); + if (!item) + continue; + } else { + item = &itm[j]; + if (item->scratchpad == 0) + continue; + assert(list_del(&head, item) != NULL); + } + item->scratchpad = 0; + k--; + } else { + item = &itm[j]; + if (item->scratchpad != 0) + continue; + + item->scratchpad = 1; + k++; + + switch ((op >> 1) & 3) { + case 0: + list_add_head(&head, item); + break; + case 1: + list_add_tail(&head, item); + break; + case 2: + case 3: + prev = NULL; + l = 0; + do { + j = prng_rand(prng) % NITEM; + prev = &itm[j]; + if (prev->scratchpad == 0 + || prev == item) + prev = NULL; + l++; + } while (!prev && l < 10); + list_add_after(&head, prev, item); + break; + default: + assert(0); + } + } + } + assert(list_count(&head) == k); + assert(list_first(&head) != NULL); + ts_hash("prng add/after/del", "84c5fc83294eabebb9808ccbba32a303c4fca084db87ed1277d2bae1f8c5bee4"); +#endif + + l = 0; +#endif + + while ((item = list_pop(&head))) { + assert(item->scratchpad != 0); + + item->scratchpad = 0; + l++; + } + assert(l == k); + assert(list_count(&head) == 0); + assert(list_first(&head) == NULL); + ts_hash("pop", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); + + list_fini(&head); + ts_ref("fini"); + ts_end(); + prng_free(prng); + printfrr("%s end\n", str(TYPE)); +} + +#undef ts_hash +#undef ts_hashx +#undef ts_hash_head +#undef ts_hash_headx + +#undef item +#undef itm +#undef itmswap +#undef head +#undef list +#undef list_head +#undef list_item +#undef list_cmp +#undef list_hash +#undef list_init +#undef list_fini +#undef list_first +#undef list_next +#undef list_next_safe +#undef list_const_first +#undef list_const_next +#undef list_last +#undef list_prev +#undef list_prev_safe +#undef list_const_last +#undef list_const_prev +#undef list_count +#undef list_add +#undef list_add_head +#undef list_add_tail +#undef list_add_after +#undef list_find +#undef list_find_lt +#undef list_find_gteq +#undef list_member +#undef list_anywhere +#undef list_del +#undef list_pop +#undef list_swap_all + +#undef REALTYPE +#undef TYPE diff --git a/tests/lib/test_typelist.py b/tests/lib/test_typelist.py new file mode 100644 index 0000000..fe3499c --- /dev/null +++ b/tests/lib/test_typelist.py @@ -0,0 +1,21 @@ +import frrtest + + +class TestTypelist(frrtest.TestMultiOut): + program = "./test_typelist" + + +TestTypelist.onesimple("LIST end") +TestTypelist.onesimple("DLIST end") +TestTypelist.onesimple("ATOMLIST end") +TestTypelist.onesimple("HEAP end") +TestTypelist.onesimple("SORTLIST_UNIQ end") +TestTypelist.onesimple("SORTLIST_NONUNIQ end") +TestTypelist.onesimple("HASH end") +TestTypelist.onesimple("HASH_collisions end") +TestTypelist.onesimple("SKIPLIST_UNIQ end") +TestTypelist.onesimple("SKIPLIST_NONUNIQ end") +TestTypelist.onesimple("RBTREE_UNIQ end") +TestTypelist.onesimple("RBTREE_NONUNIQ end") +TestTypelist.onesimple("ATOMSORT_UNIQ end") +TestTypelist.onesimple("ATOMSORT_NONUNIQ end") diff --git a/tests/lib/test_versioncmp.c b/tests/lib/test_versioncmp.c new file mode 100644 index 0000000..bb819e3 --- /dev/null +++ b/tests/lib/test_versioncmp.c @@ -0,0 +1,66 @@ +/* + * frr_version_cmp() tests + * Copyright (C) 2018 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <defaults.h> + +static const char *rel(int x) +{ + if (x < 0) + return "<"; + if (x > 0) + return ">"; + return "=="; +} + +static int fail; + +static void compare(const char *a, const char *b, int expect) +{ + int result = frr_version_cmp(a, b); + + if (expect == result) + printf("\"%s\" %s \"%s\"\n", a, rel(result), b); + else { + printf("\"%s\" %s \"%s\", expected %s!\n", a, rel(result), b, + rel(expect)); + fail = 1; + } +} + +int main(int argc, char **argv) +{ + compare("", "", 0); + compare("1", "1", 0); + compare("1.0", "1.00", 0); + compare("10.0", "1", 1); + compare("10.0", "2", 1); + compare("2.1", "10.0", -1); + compare("1.1.1", "1.1.0", 1); + compare("1.0a", "1.0", 1); + compare("1.0a", "1.0b", -1); + compare("1.0a10", "1.0a2", 1); + compare("1.00a2", "1.0a2", 0); + compare("1.00a2", "1.0a3", -1); + compare("1.0-dev", "1.0", 1); + compare("1.0~foo", "1.0", -1); + compare("1.0~1", "1.0~0", 1); + compare("1.00~1", "1.0~0", 1); + printf("final tally: %s\n", fail ? "FAILED" : "ok"); + return fail; +} diff --git a/tests/lib/test_versioncmp.py b/tests/lib/test_versioncmp.py new file mode 100644 index 0000000..8ded53b --- /dev/null +++ b/tests/lib/test_versioncmp.py @@ -0,0 +1,8 @@ +import frrtest + + +class TestVersionCmp(frrtest.TestMultiOut): + program = "./test_versioncmp" + + +TestVersionCmp.exit_cleanly() diff --git a/tests/lib/test_xref.c b/tests/lib/test_xref.c new file mode 100644 index 0000000..aa17914 --- /dev/null +++ b/tests/lib/test_xref.c @@ -0,0 +1,140 @@ +/* + * xref tests + * Copyright (C) 2020 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "xref.h" +#include "log.h" + +/* + * "lib/test_xref.c" (only 1 directory component included) + * "logging call" + * 0x00000003 (network byte order - LOG_ERR) + * 0x00000000 (network byte order - EC / zero here) + * + * note there are no '\0' terminators included for the strings + * + * SHA256 + * => 71a65ce6e81517f642c8f55fb2af6f181f7df54357913b5b577aa61a663fdd4c + * & 0f -> 0x01 'H' + * & f001 -> 0x07 '7' + * & 3e -> 0x13 'K' + * & c007 -> 0x12 'J' + * & f8 -> 0x0b 'B' + * etc. + * (for reference: base32ch[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ") + * + * (bits are consumed starting with the lowest bit, and the first character + * only consumes 4 bits and has the 5th bit at 1) + */ + +static const char *expect_uid = "H7KJB-67TBH"; +static bool test_logcall(void) +{ + zlog_err("logging call"); + + return true; +} + +static void check_xref(const struct xref *xref, bool *found, bool *error) +{ + const char *file = xref->file, *p; + + p = strrchr(file, '/'); + if (p) + file = p + 1; + + if (strcmp(file, "test_xref.c")) + return; + if (xref->type != XREFT_LOGMSG) + return; + if (strcmp(xref->func, "test_logcall")) + return; + + printf("xref: %s:%d %s() type=%d uid=%s\n", + xref->file, xref->line, xref->func, xref->type, + xref->xrefdata ? xref->xrefdata->uid : "--"); + + if (*found) { + printf("duplicate xref!\n"); + *error = true; + } + + const struct xref_logmsg *logmsg; + + logmsg = container_of(xref, struct xref_logmsg, xref); + if (strcmp(logmsg->fmtstring, "logging call")) { + printf("log message mismatch!\n"); + *error = true; + } + if (logmsg->priority != LOG_ERR || logmsg->ec != 0) { + printf("metadata mismatch!\n"); + *error = true; + } + + *found = true; + + if (!xref->xrefdata) { + printf("no unique ID?\n"); + *error = true; + return; + } + + if (strcmp(xref->xrefdata->uid, expect_uid)) { + printf("unique ID mismatch, expected %s, got %s\n", + expect_uid, xref->xrefdata->uid); + *error = true; + } +} + +static bool test_lookup(void) +{ + struct xref_block *xb; + bool found = false, error = false; + + for (xb = xref_blocks; xb; xb = xb->next) { + const struct xref * const *xrefp; + + for (xrefp = xb->start; xrefp < xb->stop; xrefp++) { + const struct xref *xref = *xrefp; + + if (!xref) + continue; + + check_xref(xref, &found, &error); + } + } + return found && !error; +} + +bool (*tests[])(void) = { + test_lookup, + test_logcall, +}; + +XREF_SETUP(); + +int main(int argc, char **argv) +{ + zlog_aux_init("NONE: ", ZLOG_DISABLED); + + for (unsigned int i = 0; i < array_size(tests); i++) + if (!tests[i]()) + return 1; + return 0; +} diff --git a/tests/lib/test_xref.py b/tests/lib/test_xref.py new file mode 100644 index 0000000..8c3db3e --- /dev/null +++ b/tests/lib/test_xref.py @@ -0,0 +1,6 @@ +import frrtest + +class TestXref(frrtest.TestMultiOut): + program = './test_xref' + +TestXref.exit_cleanly() diff --git a/tests/lib/test_zlog.c b/tests/lib/test_zlog.c new file mode 100644 index 0000000..48fa7bc --- /dev/null +++ b/tests/lib/test_zlog.c @@ -0,0 +1,62 @@ +/* + * Zlog tests. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> +#include <memory.h> +#include "log.h" +#include "network.h" + +/* maximum amount of data to hexdump */ +#define MAXDATA 16384 + +/* + * Test hexdump functionality. + * + * At the moment, not crashing is considered success. + */ +static bool test_zlog_hexdump(void) +{ + unsigned int nl = 1; + + do { + uint8_t d[nl]; + + for (unsigned int i = 0; i < nl; i++) + d[i] = frr_weak_random(); + zlog_hexdump(d, nl - 1); + + nl += 1 + (nl / 2); + } while (nl <= MAXDATA); + + return true; +} + +bool (*tests[])(void) = { + test_zlog_hexdump, +}; + +int main(int argc, char **argv) +{ + zlog_aux_init("NONE: ", ZLOG_DISABLED); + + for (unsigned int i = 0; i < array_size(tests); i++) + if (!tests[i]()) + return 1; + return 0; +} diff --git a/tests/lib/test_zlog.py b/tests/lib/test_zlog.py new file mode 100644 index 0000000..2a2d54e --- /dev/null +++ b/tests/lib/test_zlog.py @@ -0,0 +1,5 @@ +import frrtest + + +class TestZlog(frrtest.TestMultiOut): + program = "./test_zlog" diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c new file mode 100644 index 0000000..c411de7 --- /dev/null +++ b/tests/lib/test_zmq.c @@ -0,0 +1,325 @@ +/* + * ZeroMQ event test + * Copyright (C) 2017 David Lamparter, for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> +#include "memory.h" +#include "sigevent.h" +#include "frr_zmq.h" + +DEFINE_MTYPE_STATIC(LIB, TESTBUF, "zmq test buffer"); +DEFINE_MTYPE_STATIC(LIB, ZMQMSG, "zmq message"); + +static struct thread_master *master; + +static void msg_buf_free(void *data, void *hint) +{ + XFREE(MTYPE_TESTBUF, data); +} + +static int recv_delim(void *zmqsock) +{ + /* receive delim */ + zmq_msg_t zdelim; + int more; + zmq_msg_init(&zdelim); + zmq_msg_recv(&zdelim, zmqsock, 0); + more = zmq_msg_more(&zdelim); + zmq_msg_close(&zdelim); + return more; +} +static void send_delim(void *zmqsock) +{ + /* Send delim */ + zmq_msg_t zdelim; + zmq_msg_init(&zdelim); + zmq_msg_send(&zdelim, zmqsock, ZMQ_SNDMORE); + zmq_msg_close(&zdelim); +} +static void run_client(int syncfd) +{ + int i, j; + char buf[32]; + char dummy; + void *zmqctx = NULL; + void *zmqsock; + int more; + + read(syncfd, &dummy, 1); + + zmqctx = zmq_ctx_new(); + zmq_ctx_set(zmqctx, ZMQ_IPV6, 1); + + zmqsock = zmq_socket(zmqctx, ZMQ_DEALER); + if (zmq_connect(zmqsock, "tcp://127.0.0.1:17171")) { + perror("zmq_connect"); + exit(1); + } + + /* single-part */ + for (i = 0; i < 8; i++) { + snprintf(buf, sizeof(buf), "msg #%d %c%c%c", i, 'a' + i, + 'b' + i, 'c' + i); + printf("client send: %s\n", buf); + fflush(stdout); + send_delim(zmqsock); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + more = recv_delim(zmqsock); + while (more) { + zmq_recv(zmqsock, buf, sizeof(buf), 0); + printf("client recv: %s\n", buf); + size_t len = sizeof(more); + if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len)) + break; + } + } + + /* multipart */ + for (i = 2; i < 5; i++) { + printf("---\n"); + send_delim(zmqsock); + zmq_msg_t part; + for (j = 1; j <= i; j++) { + char *dyn = XMALLOC(MTYPE_TESTBUF, 32); + + snprintf(dyn, 32, "part %d/%d", j, i); + printf("client send: %s\n", dyn); + fflush(stdout); + + zmq_msg_init_data(&part, dyn, strlen(dyn) + 1, + msg_buf_free, NULL); + zmq_msg_send(&part, zmqsock, j < i ? ZMQ_SNDMORE : 0); + } + + recv_delim(zmqsock); + do { + char *data; + + zmq_msg_recv(&part, zmqsock, 0); + data = zmq_msg_data(&part); + more = zmq_msg_more(&part); + printf("client recv (more: %d): %s\n", more, data); + } while (more); + zmq_msg_close(&part); + } + + /* write callback */ + printf("---\n"); + snprintf(buf, sizeof(buf), "Done receiving"); + printf("client send: %s\n", buf); + fflush(stdout); + send_delim(zmqsock); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + /* wait for message from server */ + more = recv_delim(zmqsock); + while (more) { + zmq_recv(zmqsock, buf, sizeof(buf), 0); + printf("client recv: %s\n", buf); + size_t len = sizeof(more); + if (zmq_getsockopt(zmqsock, ZMQ_RCVMORE, &more, &len)) + break; + } + + zmq_close(zmqsock); + zmq_ctx_term(zmqctx); +} + +static struct frrzmq_cb *cb; + +static void recv_id_and_delim(void *zmqsock, zmq_msg_t *msg_id) +{ + /* receive id */ + zmq_msg_init(msg_id); + zmq_msg_recv(msg_id, zmqsock, 0); + /* receive delim */ + recv_delim(zmqsock); +} +static void send_id_and_delim(void *zmqsock, zmq_msg_t *msg_id) +{ + /* Send Id */ + zmq_msg_send(msg_id, zmqsock, ZMQ_SNDMORE); + send_delim(zmqsock); +} +static void serverwritefn(void *arg, void *zmqsock) +{ + zmq_msg_t *msg_id = (zmq_msg_t *)arg; + char buf[32] = "Test write callback"; + size_t i; + + for (i = 0; i < strlen(buf); i++) + buf[i] = toupper(buf[i]); + printf("server send: %s\n", buf); + fflush(stdout); + send_id_and_delim(zmqsock, msg_id); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + + /* send just once */ + frrzmq_thread_cancel(&cb, &cb->write); + + zmq_msg_close(msg_id); + XFREE(MTYPE_ZMQMSG, msg_id); +} +static void serverpartfn(void *arg, void *zmqsock, zmq_msg_t *msg, + unsigned partnum) +{ + static int num = 0; + int more = zmq_msg_more(msg); + char *in = zmq_msg_data(msg); + size_t i; + zmq_msg_t reply; + char *out; + + /* Id */ + if (partnum == 0) { + send_id_and_delim(zmqsock, msg); + return; + } + /* Delim */ + if (partnum == 1) + return; + + + printf("server recv part %u (more: %d): %s\n", partnum, more, in); + fflush(stdout); + + out = XMALLOC(MTYPE_TESTBUF, strlen(in) + 1); + for (i = 0; i < strlen(in); i++) + out[i] = toupper(in[i]); + out[i] = '\0'; + zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL); + zmq_msg_send(&reply, zmqsock, ZMQ_SNDMORE); + + if (more) + return; + + out = XMALLOC(MTYPE_TESTBUF, 32); + snprintf(out, 32, "msg# was %u", partnum); + zmq_msg_init_data(&reply, out, strlen(out) + 1, msg_buf_free, NULL); + zmq_msg_send(&reply, zmqsock, 0); + + zmq_msg_close(&reply); + + if (++num < 7) + return; + + /* write callback test */ + char buf[32]; + zmq_msg_t *msg_id = XMALLOC(MTYPE_ZMQMSG, sizeof(zmq_msg_t)); + recv_id_and_delim(zmqsock, msg_id); + zmq_recv(zmqsock, buf, sizeof(buf), 0); + printf("server recv: %s\n", buf); + fflush(stdout); + + frrzmq_thread_add_write_msg(master, serverwritefn, NULL, msg_id, + zmqsock, &cb); +} + +static void serverfn(void *arg, void *zmqsock) +{ + static int num = 0; + + zmq_msg_t msg_id; + char buf[32]; + size_t i; + + recv_id_and_delim(zmqsock, &msg_id); + zmq_recv(zmqsock, buf, sizeof(buf), 0); + + printf("server recv: %s\n", buf); + fflush(stdout); + for (i = 0; i < strlen(buf); i++) + buf[i] = toupper(buf[i]); + send_id_and_delim(zmqsock, &msg_id); + zmq_msg_close(&msg_id); + zmq_send(zmqsock, buf, strlen(buf) + 1, 0); + + if (++num < 4) + return; + + /* change to multipart callback */ + frrzmq_thread_cancel(&cb, &cb->read); + frrzmq_thread_cancel(&cb, &cb->write); + + frrzmq_thread_add_read_part(master, serverpartfn, NULL, NULL, zmqsock, + &cb); +} + +static void sigchld(void) +{ + printf("child exited.\n"); + frrzmq_thread_cancel(&cb, &cb->read); + frrzmq_thread_cancel(&cb, &cb->write); +} + +static struct frr_signal_t sigs[] = { + { + .signal = SIGCHLD, + .handler = sigchld, + }, +}; + +static void run_server(int syncfd) +{ + void *zmqsock; + char dummy = 0; + struct thread t; + + master = thread_master_create(NULL); + signal_init(master, array_size(sigs), sigs); + frrzmq_init(); + + zmqsock = zmq_socket(frrzmq_context, ZMQ_ROUTER); + if (zmq_bind(zmqsock, "tcp://*:17171")) { + perror("zmq_bind"); + exit(1); + } + + frrzmq_thread_add_read_msg(master, serverfn, NULL, NULL, zmqsock, &cb); + + write(syncfd, &dummy, sizeof(dummy)); + while (thread_fetch(master, &t)) + thread_call(&t); + + zmq_close(zmqsock); + frrzmq_finish(); + thread_master_free(master); + log_memstats_stderr("test"); +} + +int main(void) +{ + int syncpipe[2]; + pid_t child; + + if (pipe(syncpipe)) { + perror("pipe"); + exit(1); + } + + child = fork(); + if (child < 0) { + perror("fork"); + exit(1); + } else if (child == 0) { + run_client(syncpipe[0]); + exit(0); + } + + run_server(syncpipe[1]); + exit(0); +} diff --git a/tests/lib/test_zmq.py b/tests/lib/test_zmq.py new file mode 100644 index 0000000..5f6189d --- /dev/null +++ b/tests/lib/test_zmq.py @@ -0,0 +1,14 @@ +import frrtest +import pytest +import os + + +class TestZMQ(frrtest.TestRefOut): + program = "./test_zmq" + + @pytest.mark.skipif( + 'S["ZEROMQ_TRUE"]=""\n' not in open("../config.status").readlines(), + reason="ZEROMQ not enabled", + ) + def test_refout(self): + return super(TestZMQ, self).test_refout() diff --git a/tests/lib/test_zmq.refout b/tests/lib/test_zmq.refout new file mode 100644 index 0000000..acac505 --- /dev/null +++ b/tests/lib/test_zmq.refout @@ -0,0 +1,67 @@ +client send: msg #0 abc +server recv: msg #0 abc +client recv: MSG #0 ABC +client send: msg #1 bcd +server recv: msg #1 bcd +client recv: MSG #1 BCD +client send: msg #2 cde +server recv: msg #2 cde +client recv: MSG #2 CDE +client send: msg #3 def +server recv: msg #3 def +client recv: MSG #3 DEF +client send: msg #4 efg +server recv part 2 (more: 0): msg #4 efg +client recv: MSG #4 EFG +client recv: msg# was 2 +client send: msg #5 fgh +server recv part 2 (more: 0): msg #5 fgh +client recv: MSG #5 FGH +client recv: msg# was 2 +client send: msg #6 ghi +server recv part 2 (more: 0): msg #6 ghi +client recv: MSG #6 GHI +client recv: msg# was 2 +client send: msg #7 hij +server recv part 2 (more: 0): msg #7 hij +client recv: MSG #7 HIJ +client recv: msg# was 2 +--- +client send: part 1/2 +client send: part 2/2 +server recv part 2 (more: 1): part 1/2 +server recv part 3 (more: 0): part 2/2 +client recv (more: 1): PART 1/2 +client recv (more: 1): PART 2/2 +client recv (more: 0): msg# was 3 +--- +client send: part 1/3 +client send: part 2/3 +client send: part 3/3 +server recv part 2 (more: 1): part 1/3 +server recv part 3 (more: 1): part 2/3 +server recv part 4 (more: 0): part 3/3 +client recv (more: 1): PART 1/3 +client recv (more: 1): PART 2/3 +client recv (more: 1): PART 3/3 +client recv (more: 0): msg# was 4 +--- +client send: part 1/4 +client send: part 2/4 +client send: part 3/4 +client send: part 4/4 +server recv part 2 (more: 1): part 1/4 +server recv part 3 (more: 1): part 2/4 +server recv part 4 (more: 1): part 3/4 +server recv part 5 (more: 0): part 4/4 +client recv (more: 1): PART 1/4 +client recv (more: 1): PART 2/4 +client recv (more: 1): PART 3/4 +client recv (more: 1): PART 4/4 +client recv (more: 0): msg# was 5 +--- +client send: Done receiving +server recv: Done receiving +server send: TEST WRITE CALLBACK +client recv: TEST WRITE CALLBACK +child exited. |