summaryrefslogtreecommitdiffstats
path: root/tests/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /tests/lib
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/lib')
-rw-r--r--tests/lib/cli/.gitignore1
-rw-r--r--tests/lib/cli/common_cli.c89
-rw-r--r--tests/lib/cli/common_cli.h42
-rw-r--r--tests/lib/cli/test_cli.c76
-rw-r--r--tests/lib/cli/test_cli.in105
-rw-r--r--tests/lib/cli/test_cli.py6
-rw-r--r--tests/lib/cli/test_cli.refout.in431
-rw-r--r--tests/lib/cli/test_commands.c401
-rw-r--r--tests/lib/cli/test_commands.in216
-rw-r--r--tests/lib/cli/test_commands.py13
-rw-r--r--tests/lib/cli/test_commands.refout1007
-rw-r--r--tests/lib/cxxcompat.c109
-rw-r--r--tests/lib/fuzz_zlog.c117
-rw-r--r--tests/lib/fuzz_zlog_inputs.py28
-rw-r--r--tests/lib/northbound/test_oper_data.c403
-rw-r--r--tests/lib/northbound/test_oper_data.in1
-rw-r--r--tests/lib/northbound/test_oper_data.py5
-rw-r--r--tests/lib/northbound/test_oper_data.refout119
-rw-r--r--tests/lib/script1.lua54
-rw-r--r--tests/lib/subdir.am385
-rw-r--r--tests/lib/test_assert.c51
-rw-r--r--tests/lib/test_assert.py56
-rw-r--r--tests/lib/test_atomlist.c394
-rw-r--r--tests/lib/test_atomlist.py8
-rw-r--r--tests/lib/test_buffer.c42
-rw-r--r--tests/lib/test_checksum.c572
-rw-r--r--tests/lib/test_darr.c279
-rw-r--r--tests/lib/test_frrlua.c99
-rw-r--r--tests/lib/test_frrlua.py14
-rw-r--r--tests/lib/test_frrscript.c99
-rw-r--r--tests/lib/test_frrscript.py14
-rw-r--r--tests/lib/test_graph.c64
-rw-r--r--tests/lib/test_graph.py5
-rw-r--r--tests/lib/test_graph.refout64
-rw-r--r--tests/lib/test_grpc.cpp977
-rw-r--r--tests/lib/test_grpc.py33
-rw-r--r--tests/lib/test_heavy.c89
-rw-r--r--tests/lib/test_heavy_thread.c113
-rw-r--r--tests/lib/test_heavy_wq.c138
-rw-r--r--tests/lib/test_idalloc.c197
-rw-r--r--tests/lib/test_idalloc.py8
-rw-r--r--tests/lib/test_memory.c106
-rw-r--r--tests/lib/test_nexthop.c186
-rw-r--r--tests/lib/test_nexthop.py8
-rw-r--r--tests/lib/test_nexthop_iter.c300
-rw-r--r--tests/lib/test_nexthop_iter.py9
-rw-r--r--tests/lib/test_ntop.c73
-rw-r--r--tests/lib/test_ntop.py8
-rw-r--r--tests/lib/test_plist.c35
-rw-r--r--tests/lib/test_prefix2str.c67
-rw-r--r--tests/lib/test_prefix2str.py8
-rw-r--r--tests/lib/test_printfrr.c409
-rw-r--r--tests/lib/test_printfrr.py8
-rw-r--r--tests/lib/test_privs.c123
-rw-r--r--tests/lib/test_resolver.c68
-rw-r--r--tests/lib/test_ringbuf.c177
-rw-r--r--tests/lib/test_ringbuf.py8
-rw-r--r--tests/lib/test_segv.c67
-rw-r--r--tests/lib/test_seqlock.c114
-rw-r--r--tests/lib/test_sig.c52
-rw-r--r--tests/lib/test_skiplist.c122
-rw-r--r--tests/lib/test_srcdest_table.c422
-rw-r--r--tests/lib/test_srcdest_table.py8
-rw-r--r--tests/lib/test_stream.c57
-rw-r--r--tests/lib/test_stream.py5
-rw-r--r--tests/lib/test_stream.refout8
-rw-r--r--tests/lib/test_table.c496
-rw-r--r--tests/lib/test_table.py12
-rw-r--r--tests/lib/test_timer_correctness.c173
-rw-r--r--tests/lib/test_timer_correctness.py8
-rw-r--r--tests/lib/test_timer_performance.c86
-rw-r--r--tests/lib/test_ttable.c169
-rw-r--r--tests/lib/test_ttable.py5
-rw-r--r--tests/lib/test_ttable.refout143
-rw-r--r--tests/lib/test_typelist.c161
-rw-r--r--tests/lib/test_typelist.h822
-rw-r--r--tests/lib/test_typelist.py21
-rw-r--r--tests/lib/test_versioncmp.c53
-rw-r--r--tests/lib/test_versioncmp.py8
-rw-r--r--tests/lib/test_xref.c127
-rw-r--r--tests/lib/test_xref.py6
-rw-r--r--tests/lib/test_zlog.c49
-rw-r--r--tests/lib/test_zlog.py5
-rw-r--r--tests/lib/test_zmq.c312
-rw-r--r--tests/lib/test_zmq.py14
-rw-r--r--tests/lib/test_zmq.refout67
86 files changed, 12109 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..e0981b9
--- /dev/null
+++ b/tests/lib/cli/common_cli.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * generic CLI test helper functions
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "lib_vty.h"
+#include "log.h"
+
+#include "common_cli.h"
+
+struct event_loop *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();
+ event_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 event thread;
+ size_t yangcount;
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* master init. */
+ master = event_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 (event_fetch(master, &thread))
+ event_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..4a1de94
--- /dev/null
+++ b/tests/lib/cli/common_cli.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * generic CLI test helper functions
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#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 event_loop *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..0314735
--- /dev/null
+++ b/tests/lib/cli/test_cli.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CLI/command dummy handling tester
+ *
+ * Copyright (C) 2015 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#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..ea84120
--- /dev/null
+++ b/tests/lib/cli/test_commands.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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.
+ */
+
+#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 event_loop *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..4ad41fc
--- /dev/null
+++ b/tests/lib/cxxcompat.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * C++ compatibility compile-time smoketest
+ * Copyright (C) 2019 David Lamparter for NetDEF, Inc.
+ */
+
+#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/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 "frrevent.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..d308f8e
--- /dev/null
+++ b/tests/lib/fuzz_zlog.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * zlog fuzzer target.
+ */
+
+#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 = event_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..ec3f8ae
--- /dev/null
+++ b/tests/lib/fuzz_zlog_inputs.py
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# zlog fuzz-tester input generator
+#
+# Copyright (C) 2021 David Lamparter for NetDEF, Inc.
+
+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..f82eddd
--- /dev/null
+++ b/tests/lib/northbound/test_oper_data.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 NetDEF, Inc.
+ * Renato Westphal
+ */
+
+#include <zebra.h>
+
+#include "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "lib_vty.h"
+#include "log.h"
+#include "northbound.h"
+
+static struct event_loop *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();
+ event_master_free(master);
+
+ log_memstats(stderr, "test-nb-oper-data");
+ if (!isexit)
+ exit(0);
+}
+
+/* main routine. */
+int main(int argc, char **argv)
+{
+ struct event 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 = event_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 (event_fetch(master, &thread))
+ event_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..6c1be50
--- /dev/null
+++ b/tests/lib/subdir.am
@@ -0,0 +1,385 @@
+##############################################################################
+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_tests_lib_test_frrscript_DEPENDENCIES = copy_script
+EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua
+
+# For out-of-tree build, lua script needs to be in the build dir, rather than
+# just available somewhere in the VPATH
+copy_script: tests/lib/script1.lua
+ test -e tests/lib/script1.lua || \
+ $(INSTALL_SCRIPT) $< tests/lib/script1.lua
+
+##############################################################################
+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_darr
+tests_lib_test_darr_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_darr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_darr_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_darr_SOURCES = tests/lib/test_darr.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..4440075
--- /dev/null
+++ b/tests/lib/test_assert.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Quick test for assert()
+ * Copyright (C) 2021 David Lamparter for NetDEF, Inc.
+ */
+
+#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 "frrevent.h"
+#include "lib/sigevent.h"
+
+int main(int argc, char **argv)
+{
+ int number = 10;
+ struct event_loop *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 = event_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..b50216c
--- /dev/null
+++ b/tests/lib/test_atomlist.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc.
+ */
+
+#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..bfb4600
--- /dev/null
+++ b/tests/lib/test_buffer.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2004 Paul Jakma
+ */
+
+#include <zebra.h>
+#include <memory.h>
+#include <lib_vty.h>
+#include <buffer.h>
+
+struct event_loop *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..329ae60
--- /dev/null
+++ b/tests/lib/test_checksum.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2008 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "checksum.h"
+#include "network.h"
+#include "prng.h"
+
+struct event_loop *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_darr.c b/tests/lib/test_darr.c
new file mode 100644
index 0000000..9150aed
--- /dev/null
+++ b/tests/lib/test_darr.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * June 23 2023, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2023, LabN Consulting, L.L.C.
+ *
+ */
+#include <zebra.h>
+#include "darr.h"
+
+/*
+ * Public functions to test:
+ * [x] - darr_append
+ * [x] - darr_append_n
+ * [x] - darr_append_nz
+ * [x] - darr_cap
+ * [-] - darr_ensure_cap
+ * [x] - darr_ensure_i
+ * [x] - darr_foreach_i
+ * [x] - darr_foreach_p
+ * [x] - darr_free
+ * [x] - darr_insert
+ * [ ] - darr_insertz
+ * [x] - darr_insert_n
+ * [x] - darr_insert_nz
+ * [x] - darr_maxi
+ * [x] - darr_pop
+ * [x] - darr_push
+ * [ ] - darr_pushz
+ * [x] - darr_remove
+ * [x] - darr_remove_n
+ * [x] - darr_reset
+ * [x] - darr_setlen
+ */
+
+static void test_int(void)
+{
+ int z105[105] = {0};
+ int a1[] = {0, 1, 2, 3, 4};
+ int a2[] = {4, 3, 2, 1, 0};
+ int *da1 = NULL;
+ int *da2 = NULL;
+ int *dap;
+ uint i;
+
+ darr_ensure_i(da1, 0);
+ da1[0] = 0;
+ assert(darr_len(da1) == 1);
+ assert(darr_cap(da1) == 1);
+
+ *darr_ensure_i(da1, 1) = 1;
+ assert(darr_len(da1) == 2);
+ assert(darr_cap(da1) == 2);
+
+ darr_ensure_i(da1, 4);
+ darr_foreach_i (da1, i)
+ da1[i] = i;
+
+ assert(darr_len(da1) == 5);
+ /* minimum non-pow2 array size for long long and smaller */
+ assert(darr_cap(da1) == 8);
+ assert(!memcmp(da1, a1, sizeof(a1)));
+
+ /* reverse the numbers */
+ darr_foreach_p (da1, dap)
+ *dap = darr_end(da1) - dap - 1;
+ assert(!memcmp(da1, a2, sizeof(a2)));
+
+ darr_append_n(da1, 100);
+ darr_foreach_p (da1, dap)
+ *dap = darr_end(da1) - dap - 1;
+
+ darr_pop_n(da1, 100);
+ darr_append_nz(da1, 100);
+ assert(!memcmp(&da1[5], z105, _darr_esize(da1) * 100));
+
+ assert(darr_len(da1) == 105);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ darr_setlen(da1, 102);
+ assert(darr_len(da1) == 102);
+ assert(darr_maxi(da1) == 127);
+
+ int a3[] = { 0xdeadbeaf, 0x12345678 };
+
+ da1[0] = a3[0];
+ da1[101] = a3[1];
+ darr_remove_n(da1, 1, 100);
+ assert(darr_len(da1) == array_size(a3));
+ assert(!memcmp(da1, a3, sizeof(a3)));
+
+ da1[0] = a3[1];
+ da1[1] = a3[0];
+
+ darr_insert_n(da1, 1, 100);
+ assert(darr_len(da1) == 102);
+ assert(da1[0] == a3[1]);
+ assert(da1[101] == a3[0]);
+
+ darr_reset(da1);
+ assert(darr_len(da1) == 0);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ /* we touch the length field of the freed block here somehow */
+ darr_insert_n(da1, 100, 300);
+ assert(darr_len(da1) == 400);
+ assert(darr_cap(da1) == 512);
+
+ da1[400 - 1] = 0x0BAD;
+ *darr_insert(da1, 0) = 0xF00D;
+ assert(da1[0] == 0xF00D);
+ assert(da1[400] == 0x0BAD);
+ assert(darr_len(da1) == 401);
+ assert(darr_cap(da1) == 512);
+
+ darr_free(da1);
+ assert(da1 == NULL);
+ assert(darr_len(da1) == 0);
+ darr_setlen(da1, 0);
+ darr_reset(da1);
+ darr_free(da1);
+
+ *darr_append(da2) = 0;
+ *darr_append(da2) = 1;
+ darr_push(da2, 2);
+ darr_push(da2, 3);
+ darr_push(da2, 4);
+
+ assert(!memcmp(da2, a1, sizeof(a1)));
+
+ assert(darr_pop(da2) == 4);
+ assert(darr_pop(da2) == 3);
+ assert(darr_pop(da2) == 2);
+ assert(darr_len(da2) == 2);
+ assert(darr_pop(da2) == 1);
+ assert(darr_pop(da2) == 0);
+ assert(darr_len(da2) == 0);
+
+ darr_free(da2);
+}
+
+static void test_struct(void)
+{
+ /*
+ *uwould like to use different sizes with padding but memcmp can't be
+ *used then.
+ */
+ struct st {
+ long long a;
+ long long b;
+ };
+ struct st z102[102] = {{0, 0}};
+ struct st *da1 = NULL;
+ struct st *da2 = NULL;
+ struct st a1[] = {
+ {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4},
+ };
+ uint i;
+
+ darr_ensure_i(da1, 0);
+ da1[0].a = 0;
+ da1[0].b = 0;
+ assert(darr_len(da1) == 1);
+ assert(darr_cap(da1) == 1);
+
+ darr_ensure_i(da1, 1)->a = 1;
+ darr_ensure_i(da1, 1)->b = 1;
+ assert(darr_len(da1) == 2);
+ assert(darr_cap(da1) == 2);
+
+ darr_ensure_i(da1, 4);
+ da1[2].a = 2;
+ da1[2].b = 2;
+
+ da1[3].a = 3;
+ da1[3].b = 3;
+
+ da1[4].a = 4;
+ da1[4].b = 4;
+
+ assert(darr_len(da1) == 5);
+ /* minimum non-pow2 array size for long long and smaller */
+ assert(darr_cap(da1) == 8);
+ assert(!memcmp(da1, a1, sizeof(a1)));
+
+ darr_append_n(da1, 100);
+
+ assert(darr_len(da1) == 105);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ darr_setlen(da1, 102);
+ assert(darr_len(da1) == 102);
+ assert(darr_maxi(da1) == 127);
+
+ struct st a2[] = {
+ {0xdeadbeaf, 0xdeadbeaf},
+ {0x12345678, 0x12345678},
+ };
+ da1[0] = a2[0];
+ da1[101] = a2[1];
+ darr_remove_n(da1, 1, 100);
+ assert(darr_len(da1) == array_size(a2));
+ assert(!memcmp(da1, a2, sizeof(a2)));
+
+ da1[0] = a2[1];
+ da1[1] = a2[0];
+
+ darr_insert_n(da1, 1, 100);
+ assert(darr_len(da1) == 102);
+ darr_foreach_i (da1, i) {
+ da1[i].a = i;
+ da1[i].b = i;
+ }
+ darr_remove_n(da1, 1, 100);
+ assert(darr_len(da1) == 2);
+ darr_insert_nz(da1, 1, 100);
+ assert(!memcmp(&da1[1], z102, 100 * sizeof(da1[0])));
+ /* assert(da1[0] == a2[1]); */
+ /* assert(da1[101] == a2[0]); */
+
+ darr_reset(da1);
+ assert(darr_len(da1) == 0);
+ assert(darr_maxi(da1) == 127);
+ assert(darr_cap(da1) == 128);
+
+ /* we touch the length field of the freed block here somehow */
+ darr_insert_n(da1, 100, 300);
+
+ assert(darr_len(da1) == 400);
+ assert(darr_cap(da1) == 512);
+
+ darr_free(da1);
+ assert(da1 == NULL);
+
+ assert(darr_len(da1) == 0);
+ darr_setlen(da1, 0);
+ darr_reset(da1);
+
+ darr_free(da1);
+
+ struct st i0 = {0, 0};
+ struct st i1 = {1, 1};
+ struct st i2 = {2, 2};
+ struct st i3 = {3, 3};
+ struct st i4 = {4, 4};
+
+ *darr_append(da2) = i0;
+ *darr_append(da2) = i1;
+ darr_push(da2, i2);
+ darr_push(da2, i3);
+ darr_push(da2, i4);
+
+ assert(!memcmp(da2, a1, sizeof(a1)));
+
+ struct st p0, p1, p2, p3, p4;
+
+ p4 = darr_pop(da2);
+ p3 = darr_pop(da2);
+ p2 = darr_pop(da2);
+ p1 = darr_pop(da2);
+ p0 = darr_pop(da2);
+ assert(darr_len(da2) == 0);
+ assert(p4.a == i4.a && p4.b == i4.b);
+ assert(p3.a == i3.a && p3.b == i3.b);
+ assert(p2.a == i2.a && p2.b == i2.b);
+ assert(p1.a == i1.a && p1.b == i1.b);
+ assert(p0.a == i0.a && p0.b == i0.b);
+
+ darr_free(da2);
+}
+
+int main(int argc, char **argv)
+{
+ test_int();
+ test_struct();
+}
diff --git a/tests/lib/test_frrlua.c b/tests/lib/test_frrlua.c
new file mode 100644
index 0000000..701e171
--- /dev/null
+++ b/tests/lib/test_frrlua.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frrlua unit tests
+ * Copyright (C) 2021 Donald Lee
+ */
+
+#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;
+
+ lua_pushinteger(L, time_a);
+ time_b = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ 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..7d4746c
--- /dev/null
+++ b/tests/lib/test_frrscript.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frrscript unit tests
+ * Copyright (C) 2021 Donald Lee
+ */
+
+#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..86af02a
--- /dev/null
+++ b/tests/lib/test_graph.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test graph data structure.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Quentin Young
+ */
+#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..957ffde
--- /dev/null
+++ b/tests/lib/test_grpc.cpp
@@ -0,0 +1,977 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * May 16 2021, Christian Hopps <chopps@labn.net>
+ *
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ */
+
+#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 "frrevent.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(test_grpc_late_init, (struct event_loop * tm), (tm));
+DEFINE_KOOH(test_grpc_fini, (), ());
+
+struct vty *vty;
+
+bool mpls_enabled;
+struct event_loop *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 event *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) {
+ modpath = std::string(binpath) +
+ std::string("../../../lib/.libs");
+ grpc_module = frrmod_load("grpc:50051", modpath.c_str(),
+ _err_print, 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 = event_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, 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(test_grpc_late_init, master);
+}
+
+static void static_shutdown(void)
+{
+ hook_call(test_grpc_fini);
+ vty_close(vty);
+ vrf_terminate();
+ vty_terminate();
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+ event_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");
+ event_add_event(master, grpc_thread_stop, NULL, 0, NULL);
+
+ test_debug("client: pthread: DONE (returning)");
+
+ return NULL;
+}
+
+static void grpc_thread_start(struct event *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 event *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();
+
+ event_add_event(master, grpc_thread_start, NULL, 0, NULL);
+
+ /* Event Loop */
+ struct event thread;
+ while (event_fetch(master, &thread))
+ event_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..1e56940
--- /dev/null
+++ b/tests/lib/test_heavy.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/* 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 "frrevent.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..4fb9ebf
--- /dev/null
+++ b/tests/lib/test_heavy_thread.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/* 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 "frrevent.h"
+#include "vty.h"
+#include "command.h"
+#include "memory.h"
+#include "log.h"
+
+#include "tests.h"
+
+extern struct event_loop *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 event *thread)
+{
+ struct work_state *ws = EVENT_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 (event_should_yield(thread)) {
+ event_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;
+
+ event_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..225573a
--- /dev/null
+++ b/tests/lib/test_heavy_wq.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+/* 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 "frrevent.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 event_loop *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_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.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..aba4c35
--- /dev/null
+++ b/tests/lib/test_memory.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+#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 event_loop *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..84732d6
--- /dev/null
+++ b/tests/lib/test_nexthop.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nexthop module test.
+ *
+ * Copyright (C) 2021 by Volta Networks, Inc.
+ */
+
+#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..33ff116
--- /dev/null
+++ b/tests/lib/test_nexthop_iter.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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
+ */
+
+#include <zebra.h>
+#include "zebra/rib.h"
+#include "prng.h"
+
+struct event_loop *master;
+static int verbose;
+
+static void str_append(char **buf, const char *repr)
+{
+ if (*buf) {
+ size_t new_size = strlen(*buf) + strlen(repr) + 1;
+ *buf = realloc(*buf, new_size);
+ assert(*buf);
+ (void)strlcat(*buf, repr, new_size);
+ } else {
+ *buf = strdup(repr);
+ assert(*buf);
+ }
+}
+
+PRINTFRR(2, 3)
+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..d357047
--- /dev/null
+++ b/tests/lib/test_ntop.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frr_inet_ntop() unit test
+ * Copyright (C) 2019 David Lamparter
+ */
+
+#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..bfad766
--- /dev/null
+++ b/tests/lib/test_plist.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Simple prefix list querying tool
+ *
+ * Copyright (C) 2021 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ */
+
+#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..9d837ee
--- /dev/null
+++ b/tests/lib/test_prefix2str.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * prefix2str() unit test
+ * Copyright (C) 2019 David Lamparter
+ * Portions:
+ * Copyright (C) 2019 Cumulus Networks, Inc
+ * Quentin Young
+ */
+#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..66699ec
--- /dev/null
+++ b/tests/lib/test_printfrr.c
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * printfrr() unit test
+ * Copyright (C) 2019 David Lamparter
+ */
+
+#include "zebra.h"
+
+#include <math.h>
+
+#include "lib/printfrr.h"
+#include "lib/memory.h"
+#include "lib/prefix.h"
+#include "lib/nexthop.h"
+#include "lib/asn.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 = &ap;
+
+ 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];
+ as_t asn;
+
+ 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);
+
+ FMT_NSTD(printchk("11110000000011111010010111000011", "%b", 0xf00fa5c3));
+ FMT_NSTD(printchk("0b01011010", "%#010b", 0x5a));
+
+ 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);
+ /* ASN checks */
+ asn = 65536;
+ printchk("1.0", "%pASD", &asn);
+ asn = 65400;
+ printchk("65400", "%pASP", &asn);
+ printchk("0.65400", "%pASE", &asn);
+ printchk("65400", "%pASD", &asn);
+
+ 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..e267548
--- /dev/null
+++ b/tests/lib/test_privs.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+#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 event_loop *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..d72ff4f
--- /dev/null
+++ b/tests/lib/test_resolver.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * FRR c-ares integration test
+ * Copyright (C) 2021 David Lamparter for NetDEF, Inc.
+ */
+
+/* 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 event_loop *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..0d7fed7
--- /dev/null
+++ b/tests/lib/test_ringbuf.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Circular buffer tests.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ */
+#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..5d2f451
--- /dev/null
+++ b/tests/lib/test_segv.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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
+ */
+
+#include <zebra.h>
+#include <sigevent.h>
+#include "lib/log.h"
+#include "lib/memory.h"
+
+struct frr_signal_t sigs[] = {};
+
+struct event_loop *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 event *thread)
+{
+ func3();
+}
+
+int main(void)
+{
+ master = event_master_create(NULL);
+ signal_init(master, array_size(sigs), sigs);
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+
+ event_execute(master, threadfunc, 0, 0, NULL);
+
+ exit(0);
+}
diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c
new file mode 100644
index 0000000..288d4a8
--- /dev/null
+++ b/tests/lib/test_seqlock.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * basic test for seqlock
+ *
+ * Copyright (C) 2015 David Lamparter
+ */
+
+#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..2bd7f30
--- /dev/null
+++ b/tests/lib/test_sig.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ */
+
+#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 event_loop *master;
+struct event t;
+
+int main(void)
+{
+ master = event_master_create(NULL);
+ signal_init(master, array_size(sigs), sigs);
+
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+
+ while (event_fetch(master, &t))
+ event_call(&t);
+
+ exit(0);
+}
diff --git a/tests/lib/test_skiplist.c b/tests/lib/test_skiplist.c
new file mode 100644
index 0000000..6af7ac5
--- /dev/null
+++ b/tests/lib/test_skiplist.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ */
+
+#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..6d6c515
--- /dev/null
+++ b/tests/lib/test_srcdest_table.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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)
+ */
+
+#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 event_loop *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_and_free(&test->log, log_free);
+ 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..d38dfe0
--- /dev/null
+++ b/tests/lib/test_stream.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Simple stream test.
+ *
+ * Copyright (C) 2006 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+#include <stream.h>
+#include "frrevent.h"
+
+#include "printfrr.h"
+
+static unsigned long long ham = 0xdeadbeefdeadbeef;
+struct event_loop *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..51bfda2
--- /dev/null
+++ b/tests/lib/test_table.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Routing table test
+ * Copyright (C) 2012 OSR.
+ *
+ * This file is part of Quagga
+ */
+
+#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 event_loop *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..04c0516
--- /dev/null
+++ b/tests/lib/test_timer_correctness.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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
+ */
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "memory.h"
+#include "prng.h"
+#include "frrevent.h"
+
+#define SCHEDULE_TIMERS 800
+#define REMOVE_TIMERS 200
+
+#define TIMESTR_LEN strlen("4294967296.999999")
+
+struct event_loop *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 event **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;
+ }
+
+ event_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 event *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 event t;
+ struct timeval **alarms;
+
+ master = event_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);
+ event_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);
+ event_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 (event_fetch(master, &t))
+ event_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..3ace076
--- /dev/null
+++ b/tests/lib/test_timer_performance.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * 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
+ */
+
+#include <zebra.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "frrevent.h"
+#include "prng.h"
+
+#define SCHEDULE_TIMERS 1000000
+#define REMOVE_TIMERS 500000
+
+struct event_loop *master;
+
+static void dummy_func(struct event *thread)
+{
+}
+
+int main(int argc, char **argv)
+{
+ struct prng *prng;
+ int i;
+ struct event **timers;
+ struct timeval tv_start, tv_lap, tv_stop;
+ unsigned long t_schedule, t_remove;
+
+ master = event_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++) {
+ event_add_timer_msec(master, dummy_func, NULL, 0, &timers[i]);
+ }
+ for (i = 0; i < SCHEDULE_TIMERS; i++)
+ event_cancel(&timers[i]);
+
+ monotime(&tv_start);
+
+ for (i = 0; i < SCHEDULE_TIMERS; i++) {
+ long interval_msec;
+
+ interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS);
+ event_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;
+ event_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);
+ event_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..562ddf9
--- /dev/null
+++ b/tests/lib/test_ttable.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ASCII table generator.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ */
+#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..070a304
--- /dev/null
+++ b/tests/lib/test_typelist.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2016-2018 David Lamparter, for NetDEF, Inc.
+ */
+
+#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..80c4005
--- /dev/null
+++ b/tests/lib/test_typelist.h
@@ -0,0 +1,822 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2019 David Lamparter, for NetDEF, Inc.
+ */
+
+/* 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 & 0xFF) ^ (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_ATOMIC(REALTYPE)
+ assert(!list_member(&head, &itm[0]));
+ assert(!list_member(&head, &itm[1]));
+#endif
+
+#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..84ae06e
--- /dev/null
+++ b/tests/lib/test_versioncmp.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * frr_version_cmp() tests
+ * Copyright (C) 2018 David Lamparter for NetDEF, Inc.
+ */
+#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..fca0d47
--- /dev/null
+++ b/tests/lib/test_xref.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * xref tests
+ * Copyright (C) 2020 David Lamparter for NetDEF, Inc.
+ */
+
+#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..95d9056
--- /dev/null
+++ b/tests/lib/test_zlog.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zlog tests.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Quentin Young
+ */
+#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..2cd9d47
--- /dev/null
+++ b/tests/lib/test_zmq.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ZeroMQ event test
+ * Copyright (C) 2017 David Lamparter, for NetDEF, Inc.
+ */
+
+#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 event_loop *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_event_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_event_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 event t;
+
+ master = event_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_event_add_read_msg(master, serverfn, NULL, NULL, zmqsock, &cb);
+
+ write(syncfd, &dummy, sizeof(dummy));
+ while (event_fetch(master, &t))
+ event_call(&t);
+
+ zmq_close(zmqsock);
+ frrzmq_finish();
+ event_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.