diff options
author | Daniel Gollub <dgollub@att.com> | 2019-11-11 15:07:38 +0100 |
---|---|---|
committer | Vyatta Package Maintainers <DL-vyatta-help@att.com> | 2019-11-13 17:08:02 +0000 |
commit | 822c8f60b72cca97fb2c86db37835a60917d1c7e (patch) | |
tree | d5593a4ed1cbb8e9709411679974355a41f0289e /tacplus-daemon/test | |
download | tacplusd-822c8f60b72cca97fb2c86db37835a60917d1c7e.tar.gz tacplusd-822c8f60b72cca97fb2c86db37835a60917d1c7e.zip |
DANOS ImportHEADdebian/1.19danos/1908master
Diffstat (limited to 'tacplus-daemon/test')
-rw-r--r-- | tacplus-daemon/test/Makefile.am | 52 | ||||
-rw-r--r-- | tacplus-daemon/test/config/typical | 19 | ||||
-rw-r--r-- | tacplus-daemon/test/config/typical-broadcast | 18 | ||||
-rw-r--r-- | tacplus-daemon/test/config/typical-changed-hold-down | 18 | ||||
-rw-r--r-- | tacplus-daemon/test/parserTester.cpp | 221 | ||||
-rw-r--r-- | tacplus-daemon/test/queueTester.cpp | 143 | ||||
-rw-r--r-- | tacplus-daemon/test/serverConnectTester.cpp | 1527 | ||||
-rw-r--r-- | tacplus-daemon/test/testMain.cpp | 19 | ||||
-rw-r--r-- | tacplus-daemon/test/transactionTester.cpp | 276 | ||||
-rw-r--r-- | tacplus-daemon/test/ut_utils.c | 17 | ||||
-rw-r--r-- | tacplus-daemon/test/ut_utils.h | 189 | ||||
-rw-r--r-- | tacplus-daemon/test/utilsTester.cpp | 324 |
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); +} |