summaryrefslogtreecommitdiff
path: root/tacplus-daemon/test
diff options
context:
space:
mode:
Diffstat (limited to 'tacplus-daemon/test')
-rw-r--r--tacplus-daemon/test/Makefile.am52
-rw-r--r--tacplus-daemon/test/config/typical19
-rw-r--r--tacplus-daemon/test/config/typical-broadcast18
-rw-r--r--tacplus-daemon/test/config/typical-changed-hold-down18
-rw-r--r--tacplus-daemon/test/parserTester.cpp221
-rw-r--r--tacplus-daemon/test/queueTester.cpp143
-rw-r--r--tacplus-daemon/test/serverConnectTester.cpp1527
-rw-r--r--tacplus-daemon/test/testMain.cpp19
-rw-r--r--tacplus-daemon/test/transactionTester.cpp276
-rw-r--r--tacplus-daemon/test/ut_utils.c17
-rw-r--r--tacplus-daemon/test/ut_utils.h189
-rw-r--r--tacplus-daemon/test/utilsTester.cpp324
12 files changed, 2823 insertions, 0 deletions
diff --git a/tacplus-daemon/test/Makefile.am b/tacplus-daemon/test/Makefile.am
new file mode 100644
index 0000000..9ee991d
--- /dev/null
+++ b/tacplus-daemon/test/Makefile.am
@@ -0,0 +1,52 @@
+AM_CFLAGS = -Wall -Wextra -Werror $(HARDENING_CFLAGS) \
+ -I$(top_srcdir)/tacplus-daemon -DVERSION=\"$(VERSION)\" \
+ $(LIBTAC_CFLAGS) $(LIBTAC_EVENT_CFLAGS) $(SYSTEMD_CFLAGS) \
+ $(GLIB_CFLAGS) -DPROG="\"$(PACKAGE)\""
+
+AM_CXXFLAGS = -std=c++11 -Wall -Wextra -Werror -Wno-missing-field-initializers \
+ $(HARDENING_CXXFLAGS) \
+ $(cpputest_CFLAGS) -I$(top_srcdir)/tacplus-daemon \
+ $(LIBTAC_CFLAGS) $(LIBTAC_EVENT_CFLAGS) $(SYSTEMD_CFLAGS) \
+ $(GLIB_CFLAGS) -DVERSION=\"$(VERSION)\" -DPROG="\"$(PACKAGE)\""
+
+AM_CPPFLAGS = $(cpputest_CFLAGS)
+
+AM_LDFLAGS = $(HARDENING_CXXFLAGS)
+
+LDADD = $(cpputest_LIBS) $(LIBTAC_LIBS) $(LIBTAC_EVENT_LIBS) \
+ $(SYSTEMD_LIBS) $(GLIB_LIBS) -lpthread -lrt
+
+check_PROGRAMS = queue_tester server_connect_tester utils_tester parser_tester \
+ transaction_tester
+
+utils_tester_SOURCES = utilsTester.cpp ut_utils.c testMain.cpp ../utils.c
+utils_tester_LDADD = $(LDADD)
+
+parser_tester_SOURCES = parserTester.cpp testMain.cpp ../parser.c \
+ ../tacplus_srv_conn.c ../utils.c ../statistics.c \
+ ../dbus_service.c ../transaction.c ../queue.c \
+ ../global.c
+
+parser_tester_LDADD = $(LDADD)
+
+server_connect_tester_SOURCES = serverConnectTester.cpp testMain.cpp \
+ ut_utils.c ../tacplus_srv_conn.c ../parser.c \
+ ../utils.c ../statistics.c ../dbus_service.c \
+ ../transaction.c ../queue.c ../global.c
+server_connect_tester_LDADD = $(LDADD)
+server_connect_tester_LDFLAGS = -Wl,-wrap,tac_connect_single \
+ -Wl,-wrap,cur_mono_time \
+ -Wl,-wrap,tacplusd_go_online \
+ -Wl,-wrap,tacplusd_go_offline \
+ -Wl,-wrap,tacplusd_online
+
+queue_tester_SOURCES = queueTester.cpp testMain.cpp ../queue.c
+queue_tester_LDADD = $(LDADD)
+
+transaction_tester_SOURCES = transactionTester.cpp testMain.cpp ut_utils.c \
+ ../transaction.c ../tacplus_srv_conn.c \
+ ../parser.c ../utils.c ../statistics.c \
+ ../dbus_service.c ../queue.c ../global.c
+transaction_tester_LDADD = $(LDADD)
+
+TESTS = $(check_PROGRAMS)
diff --git a/tacplus-daemon/test/config/typical b/tacplus-daemon/test/config/typical
new file mode 100644
index 0000000..3f031ef
--- /dev/null
+++ b/tacplus-daemon/test/config/typical
@@ -0,0 +1,19 @@
+[general]
+BroadcastAccounting=false
+SetupTimeout=2
+Dscp=16
+[server1]
+Address=1.1.1.1
+Port=49
+Secret=foo
+Timeout=3
+HoldDown=0
+SourceInterface=eth0
+
+[server2]
+Address=2:2::2:2
+Port=200
+Secret=bar
+Timeout=10
+HoldDown=12
+SourceAddress=1:1::1:1
diff --git a/tacplus-daemon/test/config/typical-broadcast b/tacplus-daemon/test/config/typical-broadcast
new file mode 100644
index 0000000..a046be9
--- /dev/null
+++ b/tacplus-daemon/test/config/typical-broadcast
@@ -0,0 +1,18 @@
+[general]
+BroadcastAccounting=true
+SetupTimeout=4
+[server1]
+Address=1.1.1.2
+Port=149
+Secret=foobar
+Timeout=3
+HoldDown=0
+SourceAddress=2.2.2.2
+
+[server2]
+Address=2:2::2:2
+Port=85
+Secret=bar
+Timeout=15
+HoldDown=3600
+SourceInterface=eth3
diff --git a/tacplus-daemon/test/config/typical-changed-hold-down b/tacplus-daemon/test/config/typical-changed-hold-down
new file mode 100644
index 0000000..2eadebd
--- /dev/null
+++ b/tacplus-daemon/test/config/typical-changed-hold-down
@@ -0,0 +1,18 @@
+[general]
+BroadcastAccounting=false
+SetupTimeout=2
+[server1]
+Address=1.1.1.1
+Port=49
+Secret=foo
+Timeout=3
+HoldDown=10
+SourceInterface=eth0
+
+[server2]
+Address=2:2::2:2
+Port=200
+Secret=bar
+Timeout=10
+HoldDown=5
+SourceAddress=1:1::1:1
diff --git a/tacplus-daemon/test/parserTester.cpp b/tacplus-daemon/test/parserTester.cpp
new file mode 100644
index 0000000..b0a8362
--- /dev/null
+++ b/tacplus-daemon/test/parserTester.cpp
@@ -0,0 +1,221 @@
+/*
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "CppUTest/TestHarness.h"
+extern "C" {
+ #include <netinet/ip.h>
+ #include "parser.h"
+}
+
+TEST_GROUP(Parser)
+{
+ static void check_server_opts(struct tacplus_options::tacplus_options_server *expect_serv,
+ struct tacplus_options::tacplus_options_server *conf_serv)
+ {
+ CHECK(conf_serv != expect_serv);
+
+ UNSIGNED_LONGS_EQUAL(expect_serv->id, conf_serv->id);
+ CHECK(sockaddr_addr_equal(expect_serv->addrs->ai_addr,
+ conf_serv->addrs->ai_addr));
+ UNSIGNED_LONGS_EQUAL(get_addrinfo_port(expect_serv->addrs),
+ get_addrinfo_port(conf_serv->addrs));
+
+ if (! expect_serv->src_addrs) {
+ POINTERS_EQUAL(NULL, conf_serv->src_addrs);
+ }
+ else {
+ CHECK(sockaddr_addr_equal(expect_serv->src_addrs->ai_addr,
+ conf_serv->src_addrs->ai_addr));
+ }
+
+ if (! expect_serv->src_intf) {
+ POINTERS_EQUAL(NULL, conf_serv->src_intf);
+ }
+ else {
+ STRCMP_EQUAL(expect_serv->src_intf, conf_serv->src_intf);
+ }
+
+ STRCMP_EQUAL(expect_serv->secret, conf_serv->secret);
+ UNSIGNED_LONGS_EQUAL(expect_serv->hold_down, conf_serv->hold_down);
+ UNSIGNED_LONGS_EQUAL(expect_serv->timeout, conf_serv->timeout);
+
+#ifdef HAVE_LIBTAC_EVENT
+ CHECK(! conf_serv->session);
+#else
+ LONGS_EQUAL(-1, conf_serv->fd);
+#endif
+ }
+
+ void test_typical(struct tacplus_options *loaded_opts)
+ {
+ struct tacplus_options::tacplus_options_server *serv1, *serv2;
+
+ struct tacplus_options::tacplus_options_server expect_serv1 = {
+ .id = 0,
+ .addrs = tacplus_addrinfo("1.1.1.1", "49"),
+ .src_addrs = NULL,
+ .src_intf = "eth0",
+ .timeout = 3,
+ .hold_down = 0,
+ .secret = "foo",
+ };
+
+ struct tacplus_options::tacplus_options_server expect_serv2 = {
+ .id = 1,
+ .addrs = tacplus_addrinfo("2:2::2:2", "200"),
+ .src_addrs = tacplus_addrinfo("1:1::1:1", ""),
+ .src_intf = NULL,
+ .timeout = 10,
+ .hold_down = 12,
+ .secret = "bar",
+ };
+
+ CHECK(loaded_opts);
+
+ CHECK(!loaded_opts->broadcast);
+ UNSIGNED_LONGS_EQUAL(2, loaded_opts->setupTimeout);
+ UNSIGNED_LONGS_EQUAL(16<<2, loaded_opts->dscp);
+
+ UNSIGNED_LONGS_EQUAL(2, loaded_opts->n_servers);
+ UNSIGNED_LONGS_EQUAL(0, loaded_opts->curr_server);
+ UNSIGNED_LONGS_EQUAL(INVALID_SERVER_ID, loaded_opts->next_server);
+
+ serv1 = (struct tacplus_options::tacplus_options_server *) tacplus_server(loaded_opts, 0);
+ check_server_opts(&expect_serv1, serv1);
+
+ serv2 = (struct tacplus_options::tacplus_options_server *) tacplus_server(loaded_opts, 1);
+ check_server_opts(&expect_serv2, serv2);
+ }
+
+ void test_typical_changed_hold_down(struct tacplus_options *loaded_opts)
+ {
+ struct tacplus_options::tacplus_options_server *serv1, *serv2;
+
+ struct tacplus_options::tacplus_options_server expect_serv1 = {
+ .id = 0,
+ .addrs = tacplus_addrinfo("1.1.1.1", "49"),
+ .src_addrs = NULL,
+ .src_intf = "eth0",
+ .timeout = 3,
+ .hold_down = 10,
+ .secret = "foo",
+ };
+
+ struct tacplus_options::tacplus_options_server expect_serv2 = {
+ .id = 1,
+ .addrs = tacplus_addrinfo("2:2::2:2", "200"),
+ .src_addrs = tacplus_addrinfo("1:1::1:1", ""),
+ .src_intf = NULL,
+ .timeout = 10,
+ .hold_down = 5,
+ .secret = "bar",
+ };
+
+ CHECK(loaded_opts);
+
+ CHECK(!loaded_opts->broadcast);
+ UNSIGNED_LONGS_EQUAL(2, loaded_opts->setupTimeout);
+ UNSIGNED_LONGS_EQUAL(IPTOS_CLASS_CS6, loaded_opts->dscp);
+
+ UNSIGNED_LONGS_EQUAL(2, loaded_opts->n_servers);
+ UNSIGNED_LONGS_EQUAL(0, loaded_opts->curr_server);
+ UNSIGNED_LONGS_EQUAL(INVALID_SERVER_ID, loaded_opts->next_server);
+
+ serv1 = (struct tacplus_options::tacplus_options_server *) tacplus_server(loaded_opts, 0);
+ check_server_opts(&expect_serv1, serv1);
+
+ serv2 = (struct tacplus_options::tacplus_options_server *) tacplus_server(loaded_opts, 1);
+ check_server_opts(&expect_serv2, serv2);
+ }
+};
+
+TEST(Parser, nonExistentFile)
+{
+ struct tacplus_options *opts = tacplus_parse_options("config/UNKNOWN");
+ POINTERS_EQUAL(NULL, opts);
+}
+
+TEST(Parser, typical)
+{
+ struct tacplus_options *opts = tacplus_parse_options("config/typical");
+ test_typical(opts);
+
+ cleanup_tacplus_options(&opts);
+ POINTERS_EQUAL(NULL, opts);
+}
+
+TEST(Parser, typicalChangedHoldDown)
+{
+ struct tacplus_options *opts = tacplus_parse_options("config/typical-changed-hold-down");
+ test_typical_changed_hold_down(opts);
+
+ cleanup_tacplus_options(&opts);
+ POINTERS_EQUAL(NULL, opts);
+}
+
+TEST(Parser, reloadTypicalChangedHoldDown)
+{
+ struct tacplus_options *opts = tacplus_parse_options("config/typical");
+ test_typical(opts);
+
+ struct tacplus_options *new_opts = tacplus_parse_reload_options(
+ "config/typical-changed-hold-down", &opts);
+ POINTERS_EQUAL(NULL, opts);
+ test_typical_changed_hold_down(new_opts);
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(Parser, typicalBroadcast)
+{
+ struct tacplus_options::tacplus_options_server *serv1, *serv2;
+
+ struct tacplus_options::tacplus_options_server expect_serv1 = {
+ .id = 0,
+ .addrs = tacplus_addrinfo("1.1.1.2", "149"),
+ .src_addrs = tacplus_addrinfo("2.2.2.2", ""),
+ .src_intf = NULL,
+ .timeout = 3,
+ .hold_down = 0,
+ .secret = "foobar",
+ };
+
+ struct tacplus_options::tacplus_options_server expect_serv2 = {
+ .id = 1,
+ .addrs = tacplus_addrinfo("2:2::2:2", "85"),
+ .src_addrs = NULL,
+ .src_intf = "eth3",
+ .timeout = 15,
+ .hold_down = 3600,
+ .secret = "bar",
+ };
+
+ struct tacplus_options *opts = tacplus_parse_options("config/typical-broadcast");
+ CHECK(opts);
+
+#ifdef HAVE_LIBTAC_EVENT
+ CHECK(opts->broadcast);
+#else
+ CHECK(!opts->broadcast);
+#endif
+
+ UNSIGNED_LONGS_EQUAL(4, opts->setupTimeout);
+ UNSIGNED_LONGS_EQUAL(IPTOS_CLASS_CS6, opts->dscp);
+
+ UNSIGNED_LONGS_EQUAL(2, opts->n_servers);
+ UNSIGNED_LONGS_EQUAL(0, opts->curr_server);
+ UNSIGNED_LONGS_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ serv1 = (struct tacplus_options::tacplus_options_server *) tacplus_server(opts, 0);
+ check_server_opts(&expect_serv1, serv1);
+
+ serv2 = (struct tacplus_options::tacplus_options_server *) tacplus_server(opts, 1);
+ check_server_opts(&expect_serv2, serv2);
+
+ cleanup_tacplus_options(&opts);
+ POINTERS_EQUAL(NULL, opts);
+}
diff --git a/tacplus-daemon/test/queueTester.cpp b/tacplus-daemon/test/queueTester.cpp
new file mode 100644
index 0000000..a24818d
--- /dev/null
+++ b/tacplus-daemon/test/queueTester.cpp
@@ -0,0 +1,143 @@
+/*
+ Copyright (c) 2018 AT&T Intellectual Property.
+ Copyright (c) 2015 Brocade Communications Systems, Inc.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "CppUTest/TestHarness.h"
+extern "C" {
+ #include "queue.h"
+}
+
+struct test_elem {
+ int num;
+ void *opaque;
+};
+
+TEST_GROUP(Queueing)
+{
+ Queue *q;
+
+ void setup() {
+ q = create_queue(NULL);
+ CHECK(q);
+ }
+
+ void teardown (void) {
+ destroy_queue(&q);
+ POINTERS_EQUAL(NULL, q);
+ }
+};
+
+TEST(Queueing, EnqueueSingle)
+{
+ struct test_elem *pair = (struct test_elem *)malloc(sizeof(struct test_elem));
+ int empty;
+
+ POINTERS_EQUAL(NULL, q->front);
+ POINTERS_EQUAL(NULL, q->rear);
+
+ enqueue(q, pair);
+
+ CHECK(q->front != NULL);
+ CHECK(q->rear != NULL);
+ CHECK(q->front == q->rear);
+
+ empty = is_queue_empty(q);
+ LONGS_EQUAL(0, empty)
+
+ /* memory pointed to by pair will be freed in teardown */
+};
+
+TEST(Queueing, DequeueEmpty)
+{
+ struct test_elem *pair_consumed;
+ int empty;
+
+ POINTERS_EQUAL(NULL, q->front);
+ POINTERS_EQUAL(NULL, q->rear);
+
+ empty = is_queue_empty(q);
+ LONGS_EQUAL(1, empty);
+
+ pair_consumed = (struct test_elem *)dequeue(q);
+ POINTERS_EQUAL(NULL, pair_consumed);
+ LONGS_EQUAL(1, empty);
+};
+
+TEST(Queueing, DequeueNonEmpty)
+{
+ struct test_elem *pair = (struct test_elem *)malloc(sizeof(struct test_elem));
+ struct test_elem *pair_consumed;
+ int empty;
+
+ POINTERS_EQUAL(NULL, q->front);
+ POINTERS_EQUAL(NULL, q->rear);
+
+ enqueue(q, pair);
+
+ CHECK(q->front != NULL);
+ CHECK(q->rear != NULL);
+
+ pair_consumed = (struct test_elem *)dequeue(q);
+ POINTERS_EQUAL(pair, pair_consumed);
+
+ empty = is_queue_empty(q);
+ LONGS_EQUAL(1, empty)
+
+ free(pair);
+};
+
+TEST(Queueing, DequeueMultiple)
+{
+ struct test_elem *pair1 = (struct test_elem *)malloc(sizeof(struct test_elem));
+ struct test_elem *pair2 = (struct test_elem *)malloc(sizeof(struct test_elem));
+ struct test_elem *pair3 = (struct test_elem *)malloc(sizeof(struct test_elem));
+ struct test_elem *pair_consumed1;
+ struct test_elem *pair_consumed2;
+ struct test_elem *pair_consumed3;
+ Node *rear;
+ int empty;
+
+ POINTERS_EQUAL(NULL, q->front);
+ POINTERS_EQUAL(NULL, q->rear);
+
+ rear = q->rear;
+ enqueue(q, pair1);
+ CHECK(q->rear != rear);
+
+ CHECK(q->front != NULL);
+ CHECK(q->rear != NULL);
+ POINTERS_EQUAL(q->front, q->rear);
+
+ rear = q->rear;
+ enqueue(q, pair2);
+ CHECK(q->rear != rear);
+
+ CHECK(q->front != q->rear);
+
+ rear = q->rear;
+ enqueue(q, pair3);
+ CHECK(q->rear != rear);
+
+ CHECK(q->front != q->rear);
+
+ LONGS_EQUAL(0, empty);
+
+ pair_consumed1 = (struct test_elem *)dequeue(q);
+ POINTERS_EQUAL(pair1, pair_consumed1);
+ free(pair1);
+
+ pair_consumed2 = (struct test_elem *)dequeue(q);
+ POINTERS_EQUAL(pair2, pair_consumed2);
+ free(pair2);
+
+ pair_consumed3 = (struct test_elem *)dequeue(q);
+ POINTERS_EQUAL(pair3, pair_consumed3);
+ free(pair3);
+
+ empty = is_queue_empty(q);
+ LONGS_EQUAL(1, empty);
+};
+
diff --git a/tacplus-daemon/test/serverConnectTester.cpp b/tacplus-daemon/test/serverConnectTester.cpp
new file mode 100644
index 0000000..98654ba
--- /dev/null
+++ b/tacplus-daemon/test/serverConnectTester.cpp
@@ -0,0 +1,1527 @@
+/*
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+ Copyright (c) 2015 Brocade Communications Systems, Inc.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "CppUTest/TestHarness.h"
+extern "C" {
+ #include "tacplus_srv_conn.h"
+ #include "global.h"
+ #include "parser.h"
+ #include "statistics.h"
+}
+#include "ut_utils.h"
+
+TEST_GROUP(ServerConnection) {
+
+#define SET_OPTS_SERVER(O,I,A,P) \
+ { \
+ CHECK(I < O->n_servers); \
+ O->server[I].id = I; \
+ O->server[I].addrs = tacplus_addrinfo(A, P); \
+ SET_OPTS_SERVER_SRC_ADDR(O, I, NULL); \
+ O->server[I].timeout = (I+1)*5; \
+ O->server[I].secret = strdup(#I); \
+ }
+
+#define SET_OPTS_SERVER_SRC_ADDR(O,I,S) \
+ { \
+ O->server[I].src_addrs = S ? tacplus_addrinfo(S, "0") : NULL; \
+ }
+
+ static const int num_servers = 3;
+
+ struct tacplus_options *opts;
+
+ void setup()
+ {
+ CHECK_EQUAL(0, create_statistics(num_servers));
+
+ connControl->opts = opts = tacplus_options_alloc(num_servers);
+ CHECK(opts);
+
+ SET_OPTS_SERVER(opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(opts, 1, "2.2.2.2", "2");
+ SET_OPTS_SERVER(opts, 2, "3:3::3:3", "3");
+ }
+
+ void teardown()
+ {
+ cleanup_tacplus_options(&opts);
+ POINTERS_EQUAL(NULL, opts);
+
+ free_statistics();
+
+ ut_reset_tac_connect_wrapper();
+ }
+
+};
+
+#define PORT_STR_LEN (strlen("65535") + 1)
+
+static struct tacplus_options *copy_tacplus_options(struct tacplus_options *src)
+{
+ struct tacplus_options *new_opts = tacplus_options_alloc(src->n_servers);
+ CHECK(new_opts);
+
+ memcpy(new_opts, src, sizeof(*src));
+
+ for (unsigned i = 0; i < src->n_servers; i++) {
+ struct tacplus_options::tacplus_options_server *server = &(src->server[i]);
+
+ /* Quick and dirty copy for simple params */
+ memcpy(&new_opts->server[server->id], server, sizeof(*server));
+
+ /* Now need to properly copy params which point to allocated memory */
+ char port_str[PORT_STR_LEN];
+ int ret = snprintf(port_str, sizeof(port_str),
+ "%u", get_addrinfo_port(server->addrs));
+ CHECK(ret > 0 && ret < (int) sizeof(port_str));
+
+ SET_OPTS_SERVER(new_opts, server->id,
+ addrinfo_to_string(server->addrs), port_str);
+ if (server->src_addrs)
+ SET_OPTS_SERVER_SRC_ADDR(new_opts, server->id,
+ addrinfo_to_string(server->src_addrs));
+ }
+
+ return new_opts;
+}
+
+TEST(ServerConnection, initOptions)
+{
+ CHECK_EQUAL(num_servers, opts->n_servers);
+ CHECK_EQUAL(HIGHEST_PRIO_SERVER_ID, opts->curr_server);
+
+ for (unsigned i = 0; i < opts->n_servers; i++) {
+ struct tacplus_options::tacplus_options_server *serv = \
+ (struct tacplus_options::tacplus_options_server *) tacplus_server(opts, i);
+#ifndef HAVE_LIBTAC_EVENT
+ CHECK_EQUAL(-1, serv->fd);
+#endif
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, -1, -1);
+ }
+}
+
+TEST(ServerConnection, lookupServer)
+{
+ struct tacplus_options::tacplus_options_server *serv;
+
+ for (int i = 0; i < num_servers; i++) {
+ serv = (struct tacplus_options::tacplus_options_server *) tacplus_server(opts, i);
+ CHECK_EQUAL(i, serv->id);
+ }
+
+ /* Check out of bounds request */
+ POINTERS_EQUAL(NULL, tacplus_server(opts, num_servers));
+ POINTERS_EQUAL(NULL, tacplus_server(opts, num_servers+9999));
+ POINTERS_EQUAL(NULL, tacplus_server(opts, -1));
+}
+
+#ifndef HAVE_LIBTAC_EVENT
+TEST(ServerConnection, lookupCurrentServer)
+{
+ struct tacplus_options::tacplus_options_server *serv;
+
+ CHECK_EQUAL(0, opts->curr_server);
+ serv = (struct tacplus_options::tacplus_options_server *) tacplus_current_server(opts);
+ CHECK_EQUAL(0, serv->id);
+
+ opts->curr_server = 2;
+ serv = (struct tacplus_options::tacplus_options_server *) tacplus_current_server(opts);
+ CHECK_EQUAL(2, serv->id);
+
+ opts->curr_server = INVALID_SERVER_ID;
+ POINTERS_EQUAL(NULL, tacplus_current_server(opts));
+}
+
+TEST(ServerConnection, lookupCurrentSessionExtra)
+{
+ struct tac_session_extra extra, *ret;
+
+ CHECK_EQUAL(0, opts->curr_server);
+ ret = tacplus_current_session_extra(opts, &extra);
+
+ POINTERS_EQUAL(&extra, ret);
+ CHECK_EQUAL(0, extra.server_id);
+ POINTERS_EQUAL(&opts->server[0], extra.server);
+
+ opts->curr_server = 2;
+ ret = tacplus_current_session_extra(opts, &extra);
+
+ POINTERS_EQUAL(&extra, ret);
+ CHECK_EQUAL(2, extra.server_id);
+ POINTERS_EQUAL(&opts->server[2], extra.server);
+
+ opts->curr_server = INVALID_SERVER_ID;
+ ret = tacplus_current_session_extra(opts, &extra);
+
+ POINTERS_EQUAL(&extra, ret);
+ CHECK_EQUAL(INVALID_SERVER_ID, extra.server_id);
+ POINTERS_EQUAL(NULL, extra.server);
+}
+
+TEST(ServerConnection, connectSingleServerNoHoldDown)
+{
+ opts->n_servers = 1;
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 0, "10.10.10.10");
+
+ int fds[] = { 1, -1, 2 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(2, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectSingleServerHoldDown)
+{
+ opts->n_servers = 1;
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ opts->server[0].hold_down = 10;
+
+ int fds[] = { 1, -1, 2 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ /* Check connect calls without a secret */
+ free((void *) opts->server[0].secret);
+ opts->server[0].secret = NULL;
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Server is held down so there should be no attempt to connect */
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Expire hold down timer */
+ ut_inc_cur_mono_time(20, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(2, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = NULL,
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = NULL,
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectMultiServerNoHoldDown)
+{
+ opts->n_servers = 2;
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER(opts, 1, "127.0.0.2", "100");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 0, "10.10.20.20");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 1, "20.20.10.10");
+
+ int fds[] = { 1, -1, 2, -1, -1, 3 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(2, opts->server[1].fd)
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Both servers fail to connect */
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Back to server 0 */
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(3, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = *(opts->server[1].src_addrs->ai_addr),
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = *(opts->server[1].src_addrs->ai_addr),
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectMultiServerHoldDown)
+{
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER(opts, 1, "127.0.0.2", "100");
+ SET_OPTS_SERVER(opts, 2, "127.0.0.3", "100");
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 10;
+ opts->server[2].hold_down = 10;
+ SET_OPTS_SERVER_SRC_ADDR(opts, 0, "10.10.10.10");
+ SET_OPTS_SERVER_SRC_ADDR(opts, 2, "11.11.11.11");
+
+ int fds[] = { 1, -1, 2, -1, 3, -1, 4, 5 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(2, opts->server[1].fd)
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(3, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(-1, opts->server[2].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* All servers are held down so there should be no attempt to connect to any */
+ CHECK_FALSE(tacplus_connect());
+ CHECK_FALSE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(-1, opts->server[2].fd);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Expire hold down timers */
+ ut_inc_cur_mono_time(20, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(4, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ /* Should stick with server 0 on a subsequent connect */
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(5, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = *(opts->server[2].src_addrs->ai_addr),
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = *(opts->server[2].src_addrs->ai_addr),
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = *(opts->server[0].src_addrs->ai_addr),
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+
+TEST(ServerConnection, connectMultiServerDiffHoldDown)
+{
+ SET_OPTS_SERVER(opts, 0, "127.0.0.1", "100");
+ SET_OPTS_SERVER(opts, 1, "127.0.0.2", "100");
+ SET_OPTS_SERVER(opts, 2, "127.0.0.3", "100");
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 5;
+ opts->server[2].hold_down = 0;
+
+ int fds[] = { 1, -1, 2, -1, 3, -1, 4, 5, 6, 7 };
+ ut_set_tac_connect_fds(fds, ARRAY_SIZE(fds));
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(1, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(2, opts->server[1].fd)
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(3, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(1, opts->next_server);
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(-1, opts->server[0].fd);
+ CHECK_EQUAL(-1, opts->server[1].fd);
+ CHECK_EQUAL(-1, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(2, opts->next_server);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(4, opts->server[2].fd);
+ CHECK_EQUAL(2, opts->curr_server);
+ CHECK_EQUAL(1, opts->next_server);
+
+ /* Expire hold down timer for server 1 */
+ ut_inc_cur_mono_time(6, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(5, opts->server[1].fd);
+ CHECK_EQUAL(1, opts->curr_server);
+ CHECK_EQUAL(0, opts->next_server);
+
+ /* Expire hold down timer for server 0 */
+ ut_inc_cur_mono_time(20, 0);
+
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(6, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ /* Should stick with server 0 on a subsequent connect */
+ CHECK_TRUE(tacplus_connect());
+ CHECK_TRUE(tacplusd_online());
+ CHECK_EQUAL(7, opts->server[0].fd);
+ CHECK_EQUAL(0, opts->curr_server);
+ CHECK_EQUAL(INVALID_SERVER_ID, opts->next_server);
+
+ struct tac_connect_call exp_calls[] = {
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[2].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "2",
+ .timeout = 15,
+ },
+ {
+ .server_addr = *(opts->server[1].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "1",
+ .timeout = 10,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ {
+ .server_addr = *(opts->server[0].addrs->ai_addr),
+ .source_addr = {0},
+ .key = "0",
+ .timeout = 5,
+ },
+ };
+
+ struct tac_connect_call *calls;
+ int calls_len = ut_get_tac_connect_calls(&calls);
+ LONGS_EQUAL(ARRAY_SIZE(exp_calls), calls_len);
+
+ for (int i = 0; i < calls_len; i++)
+ CHECK_TRUE(ut_tac_connect_call_eq(&exp_calls[i], &calls[i]));
+}
+#endif
+
+/*
+ * Hold down timer tests
+ */
+
+TEST(ServerConnection, activateHoldDown)
+{
+ struct tacplus_options::tacplus_options_server *serv = &opts->server[0];
+
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, -1, -1);
+ serv->hold_down = 10;
+
+ ut_set_cur_mono_time(1245, 19828);
+ tacplus_server_activate_hold_down((struct tacplus_options_server *)serv);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, 1245, 19828);
+ CHECK(tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+
+ ut_set_cur_mono_time(97876757, 87687);
+ tacplus_server_activate_hold_down((struct tacplus_options_server *)serv);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, 97876757, 87687);
+ CHECK(tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+}
+
+TEST(ServerConnection, resetHoldDown)
+{
+ struct tacplus_options::tacplus_options_server *serv = &opts->server[0];
+
+ ut_set_cur_mono_time(110, 0);
+ serv->hold_down = 10;
+
+ SET_TIMESPEC_VALS(serv->state.lastTrouble, 105, 20221);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, 105, 20221);
+
+ CHECK(tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+
+ tacplus_server_reset_hold_down((struct tacplus_options_server *)serv);
+ CHECK_TIMESPEC_VALS(serv->state.lastTrouble, -1, -1);
+
+ CHECK(! tacplus_server_is_held_down((const struct tacplus_options_server *)serv));
+}
+
+TEST(ServerConnection, remainingHoldDown)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ struct timespec remaining;
+ bool held_down;
+
+ ut_set_cur_mono_time(20, 500);
+
+ /* No hold down configured, and no connection issues detected */
+ UNSIGNED_LONGS_EQUAL(0, serv.hold_down);
+ CHECK_TIMESPEC_VALS(serv.state.lastTrouble, 0, 0);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Configure a hold down, still no connection issues detected */
+ serv.hold_down = 10;
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Experience some connection trouble */
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 18, 99769);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 8, 99269);
+
+ /* Some time passes */
+ ut_set_cur_mono_time(24, 921978);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 3, 999177791);
+
+ /* Not quite enough time passes */
+ ut_set_cur_mono_time(28, 99768);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 1);
+
+ /* Exactly enough time passes */
+ ut_set_cur_mono_time(28, 99769);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Some more time passes */
+ ut_set_cur_mono_time(30, 10029);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+
+ /* Experience some more connection trouble */
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 30, 10029);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 10, 0);
+
+ /* Some time passes */
+ ut_set_cur_mono_time(32, 100);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 8, 9929);
+
+ /* Enough time passes to expire timer again */
+ ut_set_cur_mono_time(50, 89817);
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+}
+
+TEST(ServerConnection, remainingHoldDownConfChange)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ struct timespec remaining;
+ bool held_down;
+
+ /* Hold down timer is active */
+ ut_set_cur_mono_time(20, 500);
+ serv.hold_down = 15;
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 14, 21984);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 9, 21484);
+
+ /* Hold down timer increases */
+ serv.hold_down += 30;
+ ut_set_cur_mono_time(24, 98897);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 34, 999923087);
+
+ /* Hold down timer decreases */
+ serv.hold_down -= 21;
+ ut_set_cur_mono_time(26, 71298);
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 11, 999950686);
+
+ /* Hold down timer disabled */
+ serv.hold_down = 0;
+
+ held_down = tacplus_server_remaining_hold_down((const struct tacplus_options_server *)
+ &serv, &remaining);
+ CHECK_FALSE(held_down);
+ CHECK_TIMESPEC_VALS(remaining, 0, 0);
+}
+
+TEST(ServerConnection, isHeldDown)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ bool held_down;
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK_FALSE(held_down);
+
+ /* Hold down timer is active */
+ ut_set_cur_mono_time(30, 2480);
+ serv.hold_down = 25;
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 24, 21984);
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK(held_down);
+
+ /* Hold down timer increases */
+ serv.hold_down += 30;
+ ut_set_cur_mono_time(34, 98897);
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK(held_down);
+
+ /* Hold down timer decreases */
+ serv.hold_down -= 21;
+ ut_set_cur_mono_time(36, 71298);
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK(held_down);
+
+ /* Hold down timer disabled */
+ serv.hold_down = 0;
+
+ held_down = tacplus_server_is_held_down((const struct tacplus_options_server *) &serv);
+ CHECK_FALSE(held_down);
+}
+
+TEST(ServerConnection, remainingHoldDownSecs)
+{
+ struct tacplus_options::tacplus_options_server serv = {};
+ time_t remaining;
+
+ /* Hold down timer is active */
+ ut_set_cur_mono_time(20, 500);
+ serv.hold_down = 15;
+ SET_TIMESPEC_VALS(serv.state.lastTrouble, 14, 21984);
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(9, remaining);
+
+ /* Hold down timer increases */
+ serv.hold_down += 30;
+ ut_set_cur_mono_time(24, 98897);
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(35, remaining);
+
+ /* Hold down timer decreases */
+ serv.hold_down -= 21;
+ ut_set_cur_mono_time(26, 71298);
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(12, remaining);
+
+ /* Hold down timer disabled */
+ serv.hold_down = 0;
+
+ remaining = tacplus_server_remaining_hold_down_secs(
+ (const struct tacplus_options_server *) &serv);
+ LONGS_EQUAL(0, remaining);
+}
+
+TEST(ServerConnection, copyServerState)
+{
+ struct tacplus_options *new_opts;
+
+ /* Set state on the current opts servers */
+ SET_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ SET_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ SET_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ /* Set some config - this should not be transferred */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Exactly the same servers remain in the new opts */
+ new_opts = tacplus_options_alloc(num_servers);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "2.2.2.2", "2");
+ SET_OPTS_SERVER(new_opts, 2, "3:3::3:3", "3");
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, -1, -1);
+
+ tacplus_copy_server_state(opts, new_opts);
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, 767, 9867);
+ CHECK_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ CHECK_EQUAL(new_opts->server[0].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[1].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[2].hold_down, 0);
+}
+
+TEST(ServerConnection, copyServerStateSomeMatch)
+{
+ struct tacplus_options *new_opts;
+
+ /* Set state on the current opts servers */
+ SET_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ SET_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ SET_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ /* Set some config - this should not be transferred */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Only server ID 0 remains the same in the new opts */
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3.3.3.3", "3");
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+
+ tacplus_copy_server_state(opts, new_opts);
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ CHECK_EQUAL(new_opts->server[0].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[1].hold_down, 0);
+}
+
+TEST(ServerConnection, copyServerStateAdded)
+{
+ struct tacplus_options *new_opts;
+
+ /* Set state on the current opts servers */
+ SET_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ SET_TIMESPEC_VALS(opts->server[1].state.lastTrouble, 0, 2);
+ SET_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ /* Set some config - this should not be transferred */
+ opts->server[0].hold_down = 0;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Add an additional server to new opts */
+ new_opts = tacplus_options_alloc(4);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "2.2.2.2", "200"); // Port does not affect match
+ SET_OPTS_SERVER(new_opts, 2, "3:3::3:3", "33");
+ SET_OPTS_SERVER(new_opts, 3, "4.4.4.4", "4");
+
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(new_opts->server[3].state.lastTrouble, -1, -1);
+
+ tacplus_copy_server_state(opts, new_opts);
+
+ /* Hold down not was not previously configured - so last trouble should not be transferred */
+ CHECK_TIMESPEC_VALS(new_opts->server[0].state.lastTrouble, -1, -1);
+
+ CHECK_TIMESPEC_VALS(new_opts->server[1].state.lastTrouble, 0, 2);
+ CHECK_TIMESPEC_VALS(new_opts->server[2].state.lastTrouble, 767, 9867);
+ CHECK_TIMESPEC_VALS(new_opts->server[3].state.lastTrouble, -1, -1);
+ CHECK_TIMESPEC_VALS(opts->server[0].state.lastTrouble, 10, 5);
+ CHECK_TIMESPEC_VALS(opts->server[1].state.lastTrouble, 0, 2);
+ CHECK_TIMESPEC_VALS(opts->server[2].state.lastTrouble, 767, 9867);
+
+ CHECK_EQUAL(new_opts->server[0].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[1].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[2].hold_down, 0);
+ CHECK_EQUAL(new_opts->server[3].hold_down, 0);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsNull)
+{
+ struct tacplus_options *empty = NULL;
+
+ POINTERS_EQUAL(NULL, tacplus_reload_options(&opts, NULL));
+ POINTERS_EQUAL(NULL, opts);
+
+ POINTERS_EQUAL(NULL, tacplus_reload_options(&empty, NULL));
+ POINTERS_EQUAL(NULL, empty);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigWithTrouble)
+{
+ struct tacplus_options *opts_one, *opts_two, *opts_three;
+
+ opts_one = tacplus_options_alloc(1);
+ CHECK(opts_one);
+
+ SET_OPTS_SERVER(opts_one, 0, "1.1.1.1", "1");
+
+ /* Current server experienced trouble */
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+ LONGS_EQUAL(0, opts->server[0].hold_down);
+
+ /* Configure a hold down timer and reload */
+ opts_one->server[0].hold_down = 10;
+ ut_inc_cur_mono_time(3, 0);
+ POINTERS_EQUAL(opts_one, tacplus_reload_options(&opts, opts_one));
+ POINTERS_EQUAL(NULL, opts);
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_one->server[0]));
+
+ /* Unless we experience some more trouble on the server */
+ ut_inc_cur_mono_time(2, 0);
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts_one->server[0]);
+
+ CHECK(tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_one->server[0]));
+ LONGS_EQUAL(10, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&opts_one->server[0]));
+
+ /* Reconfigure with disabled hold down timers */
+ opts_two = tacplus_options_alloc(1);
+ CHECK(opts_two);
+
+ SET_OPTS_SERVER(opts_two, 0, "1.1.1.1", "1");
+
+ LONGS_EQUAL(0, opts_two->server[0].hold_down);
+
+ POINTERS_EQUAL(opts_two, tacplus_reload_options(&opts_one, opts_two));
+ POINTERS_EQUAL(NULL, opts_one);
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_two->server[0]));
+
+ /* Reconfigure with hold down timer once again */
+ opts_three = tacplus_options_alloc(1);
+
+ SET_OPTS_SERVER(opts_three, 0, "1.1.1.1", "1");
+ opts_three->server[0].hold_down = 3000;
+
+ POINTERS_EQUAL(opts_three, tacplus_reload_options(&opts_two, opts_three));
+ POINTERS_EQUAL(NULL, opts_two);
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts_three->server[0]));
+
+ cleanup_tacplus_options(&opts_three);
+ POINTERS_EQUAL(NULL, opts_three);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigWithoutTrouble)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ LONGS_EQUAL(0, opts->server[0].hold_down);
+
+ /* Configure a hold down timer and reload */
+ new_opts->server[0].hold_down = 10;
+ ut_inc_cur_mono_time(3, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Hold down timer should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDisable)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ LONGS_EQUAL(0, new_opts->server[0].hold_down);
+ LONGS_EQUAL(0, new_opts->server[1].hold_down);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ CHECK(tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts->server[0]));
+
+ ut_inc_cur_mono_time(10, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Hold down timers should not be activated */
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[1]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigIncrement)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ /* Increase hold down of new config */
+ new_opts->server[0].hold_down = 20;
+ new_opts->server[1].hold_down = 25;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ LONGS_EQUAL(15, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&opts->server[0]));
+
+ ut_inc_cur_mono_time(10, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ LONGS_EQUAL(10, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrement)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ /* Decrease hold down of new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ LONGS_EQUAL(15, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&opts->server[0]));
+
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ LONGS_EQUAL(5, tacplus_server_remaining_hold_down_secs(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrementBelowRemaining)
+{
+ struct tacplus_options *new_opts;
+
+ new_opts = tacplus_options_alloc(2);
+ CHECK(new_opts);
+
+ SET_OPTS_SERVER(new_opts, 0, "1.1.1.1", "1");
+ SET_OPTS_SERVER(new_opts, 1, "3:3::3:3", "3");
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 15;
+ opts->server[1].hold_down = 20;
+
+ /* Decrease hold down of new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+
+ tacplus_server_activate_hold_down(
+ (struct tacplus_options_server *)&opts->server[0]);
+
+ CHECK(tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts->server[0]));
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&opts->server[1]));
+
+ ut_inc_cur_mono_time(12, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[0]));
+ CHECK(! tacplus_server_is_held_down(
+ (struct tacplus_options_server *)&new_opts->server[1]));
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsNoHoldDownConfigOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Verify no hold down on current and new config */
+ for (unsigned i = 0; i < opts->n_servers; i++) {
+ LONGS_EQUAL(0, opts->server[i].hold_down);
+ LONGS_EQUAL(0, new_opts->server[i].hold_down);
+ }
+
+ /* No hold down - we should be online */
+ CHECK_TRUE(tacplusd_online());
+
+ /* Pass some time and reload */
+ ut_inc_cur_mono_time(10, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Still No hold down - we should still be online */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Verify no hold down on current config */
+ for (unsigned i = 0; i < opts->n_servers; i++)
+ LONGS_EQUAL(0, opts->server[i].hold_down);
+
+ /* Add hold down to new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* No hold down - we should be online */
+ CHECK_TRUE(tacplusd_online());
+
+ /* Pass some time and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Now have hold down but no trouble - we should still be online */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+static void ut_do_failing_tacplus_connect(
+ struct tacplus_options *opts, bool enforce_go_offline)
+{
+ int *fds = (int *) calloc(opts->n_servers, sizeof(int));
+ CHECK(fds);
+
+ unsigned num_servers_with_hold_down = 0;
+ for (unsigned i = 0; i < opts->n_servers; i++) {
+ if (opts->server[i].hold_down > 0)
+ num_servers_with_hold_down++;
+ fds[i] = -1;
+ }
+ ut_set_tac_connect_fds(fds, opts->n_servers);
+
+ if (enforce_go_offline) {
+ LONGS_EQUAL_TEXT(opts->n_servers, num_servers_with_hold_down,
+ "Test requires a hold down on all servers");
+ }
+ else {
+ CHECK_TEXT(num_servers_with_hold_down < opts->n_servers,
+ "Test requires no hold down on at least one server");
+ }
+
+ CHECK_FALSE(tacplus_connect());
+ CHECK_TEXT(tacplusd_online() == !enforce_go_offline,
+ "Unexpected online state");
+
+ ut_reset_tac_connect_wrapper();
+ free(fds);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigWithTroubleOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Verify no hold down on current config */
+ for (unsigned i = 0; i < opts->n_servers; i++)
+ LONGS_EQUAL(0, opts->server[i].hold_down);
+
+ /* Add hold down to new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* Experience trouble on all servers */
+ ut_do_failing_tacplus_connect(opts, false);
+
+ /* No hold down - we should be online despite trouble */
+ CHECK_TRUE(tacplusd_online());
+
+ /* Pass some time and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /*
+ * We should still be online - previous trouble is ignored on a
+ * newly configured hold down timer.
+ */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDisableOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Verify no hold down on new config */
+ for (unsigned i = 0; i < new_opts->n_servers; i++)
+ LONGS_EQUAL(0, new_opts->server[i].hold_down);
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* Hold down was removed - we should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDisableOneOnlineCheck)
+{
+ struct tacplus_options *new_opts;
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 20;
+ opts->server[1].hold_down = 25;
+ opts->server[2].hold_down = 30;
+
+ new_opts = copy_tacplus_options(opts);
+
+ /* Remove hold down from second server in new config */
+ new_opts->server[1].hold_down = 0;
+ LONGS_EQUAL(20, new_opts->server[0].hold_down);
+ LONGS_EQUAL(30, new_opts->server[2].hold_down);
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(15, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should be online again since server 2 is now available */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigIncrementOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 10;
+ opts->server[1].hold_down = 15;
+ opts->server[2].hold_down = 20;
+
+ /* Increase hold down on new config */
+ new_opts->server[0].hold_down = 20;
+ new_opts->server[1].hold_down = 25;
+ new_opts->server[2].hold_down = 30;
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should still be offline */
+ CHECK_FALSE(tacplusd_online());
+
+ /* Pass enough time that the original hold down would have expired */
+ ut_inc_cur_mono_time(6, 0);
+
+ /* We should still be offline */
+ CHECK_FALSE(tacplusd_online());
+
+ /* Pass enough time that the first server hold down expires */
+ ut_inc_cur_mono_time(10, 0);
+
+ /* We should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrementOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 20;
+ opts->server[1].hold_down = 25;
+ opts->server[2].hold_down = 30;
+
+ /* Decrease hold down on new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass some time (but still within offline period) and reload */
+ ut_inc_cur_mono_time(5, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should still be offline */
+ CHECK_FALSE(tacplusd_online());
+
+ /* Pass enough time that the new hold down expires */
+ ut_inc_cur_mono_time(6, 0);
+
+ /* We should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
+
+TEST(ServerConnection, tacplusReloadOptionsHoldDownConfigDecrementBelowRemainingOnlineCheck)
+{
+ struct tacplus_options *new_opts = copy_tacplus_options(opts);
+
+ /* Add hold down to current config */
+ opts->server[0].hold_down = 20;
+ opts->server[1].hold_down = 25;
+ opts->server[2].hold_down = 30;
+
+ /* Decrease hold down on new config */
+ new_opts->server[0].hold_down = 10;
+ new_opts->server[1].hold_down = 15;
+ new_opts->server[2].hold_down = 20;
+
+ /* Go offline */
+ ut_do_failing_tacplus_connect(opts, true);
+
+ /* Pass enough time that the new hold down expires */
+ ut_inc_cur_mono_time(15, 0);
+ POINTERS_EQUAL(new_opts, tacplus_reload_options(&opts, new_opts));
+
+ /* We should be online again */
+ CHECK_TRUE(tacplusd_online());
+
+ cleanup_tacplus_options(&new_opts);
+ POINTERS_EQUAL(NULL, new_opts);
+}
diff --git a/tacplus-daemon/test/testMain.cpp b/tacplus-daemon/test/testMain.cpp
new file mode 100644
index 0000000..c7ea462
--- /dev/null
+++ b/tacplus-daemon/test/testMain.cpp
@@ -0,0 +1,19 @@
+/*
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+ Copyright (c) 2015 Brocade Communications Systems, Inc.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "CppUTest/CommandLineTestRunner.h"
+extern "C" {
+ #include "global.h"
+}
+
+static ConnectionControl _connControl;
+ConnectionControl *connControl = &_connControl;
+
+int main(int ac, char** av)
+{
+ return CommandLineTestRunner::RunAllTests(ac, av);
+}
diff --git a/tacplus-daemon/test/transactionTester.cpp b/tacplus-daemon/test/transactionTester.cpp
new file mode 100644
index 0000000..3c16ae0
--- /dev/null
+++ b/tacplus-daemon/test/transactionTester.cpp
@@ -0,0 +1,276 @@
+/*
+ Copyright (c) 2019 AT&T Intellectual Property.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "CppUTest/TestHarness.h"
+extern "C" {
+ #include <libtac.h>
+ #include "transaction.h"
+ #include "transaction_private.h"
+ #include "utils.h"
+}
+#include "ut_utils.h"
+
+TEST_GROUP(Transaction) {};
+
+TEST(Transaction, NewAttribInvalid)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new(NULL);
+ POINTERS_EQUAL(NULL, attr);
+
+ attr = transaction_attrib_new("");
+ POINTERS_EQUAL(NULL, attr);
+
+ attr = transaction_attrib_new("=");
+ POINTERS_EQUAL(NULL, attr);
+
+ attr = transaction_attrib_new("*");
+ POINTERS_EQUAL(NULL, attr);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribNoSep)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("foobarbaz");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("foobarbaz=", attr->name);
+ STRCMP_EQUAL("", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribShortNoSep)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("f");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("f=", attr->name);
+ STRCMP_EQUAL("", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribMandatory)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("cmd=set foo");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("cmd=", attr->name);
+ STRCMP_EQUAL("set foo", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribMandatoryShortNoVal)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("c=");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("c=", attr->name);
+ STRCMP_EQUAL("", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribOptional)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("cmd*set foo");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("cmd*", attr->name);
+ STRCMP_EQUAL("set foo", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribOptionalShortNoVal)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("c*");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("c*", attr->name);
+ STRCMP_EQUAL("", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribMandatoryWithOptSepInValue)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("cmd=set f*o");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("cmd=", attr->name);
+ STRCMP_EQUAL("set f*o", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribOptionalWithMandSepInValue)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("cmd*set foo=bar");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("cmd*", attr->name);
+ STRCMP_EQUAL("set foo=bar", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, NewAttribMulti)
+{
+ struct transaction_attrib *attr;
+
+ attr = transaction_attrib_new("cmd*set foo");
+ CHECK(attr != NULL);
+
+ STRCMP_EQUAL("cmd*", attr->name);
+ STRCMP_EQUAL("set foo", attr->value);
+ POINTERS_EQUAL(NULL, attr->next);
+
+ attr->next = transaction_attrib_new("cmd=delete foo");
+ CHECK(attr->next != NULL);
+
+ STRCMP_EQUAL("cmd=", attr->next->name);
+ STRCMP_EQUAL("delete foo", attr->next->value);
+ POINTERS_EQUAL(NULL, attr->next->next);
+
+ transaction_attrib_free(&attr);
+ POINTERS_EQUAL(NULL, attr);
+};
+
+TEST(Transaction, TransactionAttribFromTacAttribNull)
+{
+ POINTERS_EQUAL(NULL, transaction_attrib_from_tac_attrib(NULL));
+}
+
+static void compare_transaction_attribs(struct transaction_attrib exp[],
+ unsigned exp_len,
+ struct transaction_attrib *act)
+{
+ unsigned i;
+ for (i = 0; act; act = act->next, i++) {
+ STRCMP_EQUAL(exp[i].name, act->name);
+ STRCMP_EQUAL(exp[i].value, act->value);
+
+ if (act->next == NULL) {
+ LONGS_EQUAL_TEXT(
+ exp_len-1, i, "transaction_attrib list unexpectedly short");
+ }
+ else {
+ CHECK_TEXT(
+ i < exp_len-1, "transaction_attrib list unexpectedly long");
+ }
+ }
+
+ /* Final sanity check */
+ LONGS_EQUAL_TEXT(exp_len, i, "transaction_attrib list length mismatch");
+}
+
+TEST(Transaction, TransactionAttribFromTacAttribSingle)
+{
+ struct tac_attrib *tac_attrib = NULL;
+
+ tac_add_attrib(&tac_attrib, (char *)"cmd", (char *)"set foo bar");
+ CHECK(tac_attrib != NULL);
+
+ struct transaction_attrib *attrib;
+ attrib = transaction_attrib_from_tac_attrib(tac_attrib);
+ CHECK(attrib != NULL);
+
+ struct transaction_attrib exp_attrib[] = {
+ {
+ .next = NULL,
+ .name = "cmd=",
+ .value = "set foo bar",
+ },
+ };
+ compare_transaction_attribs(exp_attrib, ARRAY_SIZE(exp_attrib), attrib);
+
+ transaction_attrib_free(&attrib);
+ POINTERS_EQUAL(NULL, attrib);
+}
+
+TEST(Transaction, TransactionAttribFromTacAttribMulti)
+{
+ struct tac_attrib *tac_attrib = NULL;
+
+ tac_add_attrib(&tac_attrib, (char *)"cmd", (char *)"set");
+ CHECK(tac_attrib != NULL);
+ tac_add_attrib(&tac_attrib, (char *)"cmd-arg", (char *)"foo");
+ tac_add_attrib(&tac_attrib, (char *)"cmd-arg", (char *)"bar");
+ tac_add_attrib(&tac_attrib, (char *)"cmd-arg", (char *)"baz");
+
+ struct transaction_attrib *attrib;
+ attrib = transaction_attrib_from_tac_attrib(tac_attrib);
+ CHECK(attrib != NULL);
+
+ /*
+ * "next" member is unused by the tests, it is initialised to
+ * silence the C++ compiler.
+ */
+ struct transaction_attrib exp_attrib[] = {
+ {
+ .next = NULL,
+ .name = "cmd=",
+ .value = "set",
+ },
+ {
+ .next = NULL,
+ .name = "cmd-arg=",
+ .value = "foo",
+ },
+ {
+ .next = NULL,
+ .name = "cmd-arg=",
+ .value = "bar",
+ },
+ {
+ .next = NULL,
+ .name = "cmd-arg=",
+ .value = "baz",
+ },
+ };
+ compare_transaction_attribs(exp_attrib, ARRAY_SIZE(exp_attrib), attrib);
+
+ transaction_attrib_free(&attrib);
+ POINTERS_EQUAL(NULL, attrib);
+}
diff --git a/tacplus-daemon/test/ut_utils.c b/tacplus-daemon/test/ut_utils.c
new file mode 100644
index 0000000..107656d
--- /dev/null
+++ b/tacplus-daemon/test/ut_utils.c
@@ -0,0 +1,17 @@
+/*
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "ut_utils.h"
+
+int *_tac_connect_fds = NULL;
+int _tac_connect_fds_len = 0;
+
+struct tac_connect_call *_tac_connect_calls = NULL;
+int _tac_connect_call_count = 0;
+
+struct timespec _cur_time = { 0, 0 };
+
+struct timespec _offline_until = { 0, 0 };
diff --git a/tacplus-daemon/test/ut_utils.h b/tacplus-daemon/test/ut_utils.h
new file mode 100644
index 0000000..b16e4b5
--- /dev/null
+++ b/tacplus-daemon/test/ut_utils.h
@@ -0,0 +1,189 @@
+/*
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include <assert.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "utils.h"
+
+#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
+
+extern int *_tac_connect_fds;
+extern int _tac_connect_fds_len;
+
+struct tac_connect_call {
+ struct sockaddr server_addr;
+ struct sockaddr source_addr;
+ const char *key;
+ int timeout;
+};
+
+extern struct tac_connect_call *_tac_connect_calls;
+extern int _tac_connect_call_count;
+
+bool
+ut_tac_connect_call_eq(struct tac_connect_call *a, struct tac_connect_call *b)
+{
+ if (a->timeout != b->timeout)
+ return false;
+
+ if (a->key && b->key) {
+ if (strcmp(a->key, b->key) != 0)
+ return false;
+ }
+ else if (! (!a->key && !b->key)) {
+ return false;
+ }
+
+ if (memcmp(&a->server_addr, &b->server_addr, sizeof a->server_addr))
+ return false;
+
+ if (memcmp(&a->source_addr, &b->source_addr, sizeof a->source_addr))
+ return false;
+
+ return true;
+}
+
+void
+ut_reset_tac_connect_wrapper()
+{
+ for (int i = 0; i < _tac_connect_fds_len; i++)
+ free((void *)_tac_connect_calls[i].key);
+
+ free(_tac_connect_calls);
+ _tac_connect_calls = NULL;
+ _tac_connect_call_count = 0;
+
+ _tac_connect_fds = NULL;
+ _tac_connect_fds_len = 0;
+}
+
+void
+ut_set_tac_connect_fds(int *fds, int fds_len)
+{
+ _tac_connect_fds = fds;
+ _tac_connect_fds_len = fds_len;
+
+ assert(! _tac_connect_calls);
+ _tac_connect_calls = (struct tac_connect_call *) calloc(_tac_connect_fds_len,
+ sizeof(struct tac_connect_call));
+ assert(_tac_connect_calls);
+}
+
+int
+ut_get_tac_connect_calls(struct tac_connect_call **calls)
+{
+ *calls = _tac_connect_calls;
+ return _tac_connect_call_count;
+}
+
+/*
+ * Wrapper function for tac_connect_single()
+ *
+ * Use ut_set_tac_connect_fds() to set an array of return values for this
+ * function.
+ *
+ * Use ut_get_tac_connect_calls() to retrieve an array of tac_connect_call
+ * structs with the arguments from each call to tac_connect_single().
+ */
+int
+__wrap_tac_connect_single(const struct addrinfo *server,
+ const char *key,
+ struct addrinfo *srcaddr,
+ int timeout)
+{
+ if (! _tac_connect_fds)
+ return 9999;
+
+ assert(_tac_connect_call_count < _tac_connect_fds_len);
+
+ struct tac_connect_call *call = &_tac_connect_calls[_tac_connect_call_count];
+ if (server)
+ call->server_addr = *server->ai_addr;
+
+ if (srcaddr)
+ call->source_addr = *srcaddr->ai_addr;
+
+ call->key = key ? strdup(key) : NULL;
+ call->timeout = timeout;
+
+ return _tac_connect_fds[_tac_connect_call_count++];
+}
+
+#define CHECK_TIMESPEC_VALS(T,S,N) \
+ { \
+ LONGS_EQUAL(S, T.tv_sec); \
+ LONGS_EQUAL(N, T.tv_nsec); \
+ }
+
+extern struct timespec _cur_time;
+
+void
+__wrap_cur_mono_time(struct timespec *ts)
+{
+ *ts = _cur_time;
+}
+
+void
+ut_set_cur_mono_time(time_t sec, long nsec)
+{
+ _cur_time.tv_sec = sec;
+ _cur_time.tv_nsec = nsec;
+}
+
+void
+ut_inc_cur_mono_time(time_t sec, long nsec)
+{
+ ut_set_cur_mono_time(_cur_time.tv_sec + sec,
+ _cur_time.tv_nsec + nsec);
+}
+
+/*
+ * Wrapper around the online/offline APIs
+ *
+ * Piggybacking on the simulated UT "time" (_cur_time) gives a simple mechanism
+ * to simulate the service going on and offline in UTs.
+ *
+ * Upon request to go offline we store the "time" we expect to go back online
+ * ie. the current UT "time" plus the offline interval.
+ *
+ * Conversely on a request to go online we store the current UT "time".
+ *
+ * To determine whether we are on or offline we then simply check whether
+ * the current UT "time" is past the stored time (ie. when we expected to be
+ * offline until).
+ */
+extern struct timespec _offline_until;
+
+bool
+__wrap_tacplusd_go_online() {
+ _offline_until = _cur_time;
+ return true;
+}
+
+bool
+__wrap_tacplusd_go_offline(const struct timespec *ts) {
+ struct timespec ts_copy = *ts;
+ timespec_normalise(&ts_copy);
+
+ _offline_until = _cur_time;
+ timespec_normalise(&_offline_until);
+
+ _offline_until.tv_sec += ts_copy.tv_sec;
+ _offline_until.tv_nsec += ts_copy.tv_nsec;
+ timespec_normalise(&_offline_until);
+
+ return true;
+}
+
+bool
+__wrap_tacplusd_online() {
+ return timespec_cmp(&_cur_time, &_offline_until) >= 0;
+}
diff --git a/tacplus-daemon/test/utilsTester.cpp b/tacplus-daemon/test/utilsTester.cpp
new file mode 100644
index 0000000..c2ae92f
--- /dev/null
+++ b/tacplus-daemon/test/utilsTester.cpp
@@ -0,0 +1,324 @@
+/*
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+ SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#include "CppUTest/TestHarness.h"
+extern "C" {
+ #include "utils.h"
+}
+#include "ut_utils.h"
+
+TEST_GROUP(Utils) {};
+
+TEST(Utils, tacplusAddrInfoV4)
+{
+ struct addrinfo *addr;
+ char addr_str[INET_ADDRSTRLEN];
+ char port_str[6];
+ int ret;
+
+ addr = tacplus_addrinfo("192.168.122.7", "66");
+ CHECK(addr != NULL);
+
+ ret = getnameinfo(addr->ai_addr, addr->ai_addrlen,
+ addr_str, INET_ADDRSTRLEN,
+ port_str, sizeof port_str,
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ CHECK(ret == 0);
+
+ STRCMP_EQUAL("192.168.122.7", addr_str);
+ STRCMP_EQUAL("66", port_str);
+
+ freeaddrinfo(addr);
+};
+
+TEST(Utils, tacplusAddrInfoV6)
+{
+ struct addrinfo *addr;
+ char addr_str[INET6_ADDRSTRLEN];
+ char port_str[6];
+ int ret;
+
+ addr = tacplus_addrinfo("fe80::177", "49");
+ CHECK(addr != NULL);
+
+ ret = getnameinfo(addr->ai_addr, addr->ai_addrlen,
+ addr_str, INET6_ADDRSTRLEN,
+ port_str, sizeof port_str,
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ CHECK(ret == 0);
+
+ STRCMP_EQUAL("fe80::177", addr_str);
+ STRCMP_EQUAL("49", port_str);
+
+ freeaddrinfo(addr);
+};
+
+TEST(Utils, tacplusAddrInfoInvalid)
+{
+ struct addrinfo *addr;
+
+ addr = tacplus_addrinfo("invalid address", "invalid port");
+ POINTERS_EQUAL(NULL, addr);
+};
+
+TEST(Utils, strOrNil)
+{
+ const char *str;
+
+ str = strOrNil(NULL);
+ CHECK_EQUAL("(nil)", str);
+
+ str = strOrNil("foo");
+ STRCMP_EQUAL("foo", str);
+}
+
+TEST(Utils, addrinfoToString)
+{
+ struct addrinfo *info;
+ char *addr_str;
+
+ info = tacplus_addrinfo("1.1.1.1", "50");
+ CHECK(info);
+ addr_str = addrinfo_to_string(info);
+ STRCMP_EQUAL("1.1.1.1", addr_str);
+ freeaddrinfo(info);
+ free(addr_str);
+
+ info = tacplus_addrinfo("2:2::2:2", "55");
+ CHECK(info);
+ addr_str = addrinfo_to_string(info);
+ STRCMP_EQUAL("2:2::2:2", addr_str);
+ freeaddrinfo(info);
+ free(addr_str);
+}
+
+TEST(Utils, getAddrinfoPort)
+{
+ struct addrinfo *info;
+ uint16_t port;
+
+ info = tacplus_addrinfo("1.1.1.1", "50");
+ CHECK(info);
+ port = get_addrinfo_port(info);
+ CHECK_EQUAL(50, port);
+ freeaddrinfo(info);
+
+ info = tacplus_addrinfo("2:2::2:2", "65535");
+ CHECK(info);
+ port = get_addrinfo_port(info);
+ CHECK_EQUAL(65535, port);
+ freeaddrinfo(info);
+}
+
+TEST(Utils, timespecValsEq)
+{
+ struct timespec ts = { 10, 908987 };
+ CHECK(TIMESPEC_VALS_EQ(ts, 10, 908987));
+}
+
+TEST(Utils, setTimespecVals)
+{
+ struct timespec ts = {};
+ CHECK(TIMESPEC_VALS_EQ(ts, 0, 0));
+
+ SET_TIMESPEC_VALS(ts, 55, 9824798);
+ CHECK(TIMESPEC_VALS_EQ(ts, 55, 9824798));
+}
+
+/*
+ * timespec_normalise() tests
+ */
+
+TEST(Utils, timespecNormaliseNoOp)
+{
+ struct timespec ts = { 1, 500 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, 1, 500);
+}
+
+TEST(Utils, timespecNormaliseMultiWholeSec)
+{
+ struct timespec ts = { 890, 10000000000 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, 900, 0);
+}
+
+TEST(Utils, timespecNormaliseMultiSec)
+{
+ struct timespec ts = { 123210, 6674000000 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, 123216, 674000000);
+}
+
+TEST(Utils, timespecNormaliseNeg)
+{
+ struct timespec ts = { 67, -234 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, 66, 999999766);
+}
+
+TEST(Utils, timespecNormaliseMultiWholeNegSec)
+{
+ struct timespec ts = { 27, -4000000000 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, 23, 0);
+}
+
+TEST(Utils, timespecNormaliseMultiNegSec)
+{
+ struct timespec ts = { 12, -3000004413 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, 8, 999995587);
+}
+
+
+TEST(Utils, timespecNormaliseNegSecNoOp)
+{
+ struct timespec ts = { -1, 500 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, -1, 500);
+}
+
+TEST(Utils, timespecNormaliseNegMultiWholeSec)
+{
+ struct timespec ts = { -890, 10000000000 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, -880, 0);
+}
+
+TEST(Utils, timespecNormaliseNegMultiSec)
+{
+ struct timespec ts = { -123210, 6540674000 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, -123204, 540674000);
+}
+
+TEST(Utils, timespecNormaliseNegNeg)
+{
+ struct timespec ts = { -67, -234 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, -68, 999999766);
+}
+
+TEST(Utils, timespecNormaliseNegMultiWholeNegSec)
+{
+ struct timespec ts = { -27, -4000000000 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, -31, 0);
+}
+
+TEST(Utils, timespecNormaliseNegMultiNegSec)
+{
+ struct timespec ts = { -12, -3097604413 }, *ret;
+
+ ret = timespec_normalise(&ts);
+ POINTERS_EQUAL(&ts, ret);
+ CHECK_TIMESPEC_VALS(ts, -16, 902395587);
+}
+
+/*
+ * timespec_cmp() tests
+ */
+
+TEST(Utils, timespecCmpEq)
+{
+ struct timespec a = { 1, 200 };
+ struct timespec b = a;
+
+ CHECK_EQUAL(timespec_cmp(&a, &b), 0);
+ CHECK_EQUAL(timespec_cmp(&b, &a), 0);
+}
+
+TEST(Utils, timespecCmpDiffSecs)
+{
+ struct timespec a = { 1, 200 };
+ struct timespec b = { 10, 200 };
+
+ CHECK_EQUAL(timespec_cmp(&a, &b), -1);
+ CHECK_EQUAL(timespec_cmp(&b, &a), 1);
+}
+
+TEST(Utils, timespecCmpDiffNsecs)
+{
+ struct timespec a = { 188, 800 };
+ struct timespec b = { 188, 89787 };
+
+ CHECK_EQUAL(timespec_cmp(&a, &b), -1);
+ CHECK_EQUAL(timespec_cmp(&b, &a), 1);
+}
+
+TEST(Utils, timespecCmpDiffSecsNsecs)
+{
+ struct timespec a = { 12, 12786 };
+ struct timespec b = { 90, 82174 };
+
+ CHECK_EQUAL(timespec_cmp(&a, &b), -1);
+ CHECK_EQUAL(timespec_cmp(&b, &a), 1);
+
+ a.tv_nsec = b.tv_nsec + 10;
+ CHECK_EQUAL(timespec_cmp(&a, &b), -1);
+ CHECK_EQUAL(timespec_cmp(&b, &a), 1);
+}
+
+/*
+ * timespec_sub() tests
+ */
+
+TEST(Utils, timespecSubEq)
+{
+ struct timespec a = { 1, 200 };
+ struct timespec b = a;
+ struct timespec res;
+
+ timespec_sub(&a, &b, &res);
+ CHECK_TIMESPEC_VALS(res, 0, 0);
+ CHECK_TIMESPEC_VALS(a, 1, 200);
+ CHECK_TIMESPEC_VALS(b, 1, 200);
+
+ timespec_sub(&b, &a, &res);
+ CHECK_TIMESPEC_VALS(res, 0, 0);
+ CHECK_TIMESPEC_VALS(a, 1, 200);
+ CHECK_TIMESPEC_VALS(b, 1, 200);
+}
+
+TEST(Utils, timespecSub)
+{
+ struct timespec a = { 12, 98200 };
+ struct timespec b = { 4, 999098654 };
+ struct timespec res;
+
+ timespec_sub(&a, &b, &res);
+ CHECK_TIMESPEC_VALS(res, 7, 999546);
+ CHECK_TIMESPEC_VALS(a, 12, 98200);
+ CHECK_TIMESPEC_VALS(b, 4, 999098654);
+
+ timespec_sub(&b, &a, &res);
+ CHECK_TIMESPEC_VALS(res, -8, 999000454);
+ CHECK_TIMESPEC_VALS(a, 12, 98200);
+ CHECK_TIMESPEC_VALS(b, 4, 999098654);
+}