diff options
-rw-r--r-- | src/or/connection.c | 8 | ||||
-rw-r--r-- | src/or/connection.h | 3 | ||||
-rw-r--r-- | src/or/main.c | 4 | ||||
-rw-r--r-- | src/or/main.h | 2 | ||||
-rw-r--r-- | src/test/include.am | 1 | ||||
-rw-r--r-- | src/test/test.c | 1 | ||||
-rw-r--r-- | src/test/test.h | 1 | ||||
-rw-r--r-- | src/test/test_oos.c | 248 |
8 files changed, 261 insertions, 7 deletions
diff --git a/src/or/connection.c b/src/or/connection.c index 9c80d97e4d..844ab40b6e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -4550,8 +4550,8 @@ oos_victim_comparator(const void **a_v, const void **b_v) /** Pick n victim connections for the OOS handler and return them in a * smartlist. */ -static smartlist_t * -pick_oos_victims(int n) +MOCK_IMPL(STATIC smartlist_t *, +pick_oos_victims, (int n)) { smartlist_t *eligible = NULL, *victims = NULL; smartlist_t *conns; @@ -4639,8 +4639,8 @@ pick_oos_victims(int n) } /** Kill a list of connections for the OOS handler. */ -static void -kill_conn_list_for_oos(smartlist_t *conns) +MOCK_IMPL(STATIC void, +kill_conn_list_for_oos, (smartlist_t *conns)) { if (!conns) return; diff --git a/src/or/connection.h b/src/or/connection.h index 4e8a740601..83ee76932b 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -267,6 +267,9 @@ MOCK_DECL(STATIC int,connection_connect_sockaddr, const struct sockaddr *bindaddr, socklen_t bindaddr_len, int *socket_error)); +MOCK_DECL(STATIC void, kill_conn_list_for_oos, (smartlist_t *conns)); +MOCK_DECL(STATIC smartlist_t *, pick_oos_victims, (int n)); + #endif #endif diff --git a/src/or/main.c b/src/or/main.c index 30041c5e2b..7f00cc3455 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -652,8 +652,8 @@ close_closeable_connections(void) } /** Count moribund connections for the OOS handler */ -int -connection_count_moribund(void) +MOCK_IMPL(int, +connection_count_moribund, (void)) { int i, moribund = 0; connection_t *conn; diff --git a/src/or/main.h b/src/or/main.h index 4f58f170d5..45c3b3ff78 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -47,7 +47,7 @@ MOCK_DECL(void,connection_start_writing,(connection_t *conn)); void connection_stop_reading_from_linked_conn(connection_t *conn); -int connection_count_moribund(void); +MOCK_DECL(int, connection_count_moribund, (void)); void directory_all_unreachable(time_t now); void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs); diff --git a/src/test/include.am b/src/test/include.am index d0bc808877..2e91f7c9dc 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -103,6 +103,7 @@ src_test_test_SOURCES = \ src/test/test_microdesc.c \ src/test/test_nodelist.c \ src/test/test_oom.c \ + src/test/test_oos.c \ src/test/test_options.c \ src/test/test_policy.c \ src/test/test_procmon.c \ diff --git a/src/test/test.c b/src/test/test.c index f8610168f6..2f10c7e90b 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1210,6 +1210,7 @@ struct testgroup_t testgroups[] = { { "link-handshake/", link_handshake_tests }, { "nodelist/", nodelist_tests }, { "oom/", oom_tests }, + { "oos/", oos_tests }, { "options/", options_tests }, { "policy/" , policy_tests }, { "procmon/", procmon_tests }, diff --git a/src/test/test.h b/src/test/test.h index 6744d255f1..c0643e154d 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -202,6 +202,7 @@ extern struct testcase_t logging_tests[]; extern struct testcase_t microdesc_tests[]; extern struct testcase_t nodelist_tests[]; extern struct testcase_t oom_tests[]; +extern struct testcase_t oos_tests[]; extern struct testcase_t options_tests[]; extern struct testcase_t policy_tests[]; extern struct testcase_t procmon_tests[]; diff --git a/src/test/test_oos.c b/src/test/test_oos.c new file mode 100644 index 0000000000..48e66fda53 --- /dev/null +++ b/src/test/test_oos.c @@ -0,0 +1,248 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Unit tests for OOS handler */ + +#define CONNECTION_PRIVATE + +#include "or.h" +#include "config.h" +#include "connection.h" +#include "main.h" +#include "test.h" + +static or_options_t mock_options; + +static void +reset_options_mock(void) +{ + memset(&mock_options, 0, sizeof(or_options_t)); +} + +static const or_options_t * +mock_get_options(void) +{ + return &mock_options; +} + +static int moribund_calls = 0; +static int moribund_conns = 0; + +static int +mock_connection_count_moribund(void) +{ + ++moribund_calls; + + return moribund_conns; +} + +/* + * For unit test purposes it's sufficient to tell that + * kill_conn_list_for_oos() was called with an approximately + * sane argument; it's just the thing we returned from the + * mock for pick_oos_victims(). + */ + +static int kill_conn_list_calls = 0; +static int kill_conn_list_killed = 0; + +static void +kill_conn_list_mock(smartlist_t *conns) +{ + ++kill_conn_list_calls; + + tt_assert(conns != NULL); + + kill_conn_list_killed += smartlist_len(conns); + + done: + return; +} + +static int pick_oos_mock_calls = 0; +static int pick_oos_mock_fail = 0; +static int pick_oos_mock_last_n = 0; + +static smartlist_t * +pick_oos_victims_mock(int n) +{ + smartlist_t *l; + int i; + + ++pick_oos_mock_calls; + + tt_int_op(n, OP_GT, 0); + + if (!pick_oos_mock_fail) { + /* + * connection_handle_oos() just passes the list onto + * kill_conn_list_for_oos(); we don't need to simulate + * its content for this mock, just its existence, but + * we do need to check the parameter. + */ + l = smartlist_new(); + for (i = 0; i < n; ++i) smartlist_add(l, NULL); + } else { + l = NULL; + } + + pick_oos_mock_last_n = n; + + done: + return l; +} + +/** Unit test for the logic in connection_handle_oos(), which is concerned + * with comparing thresholds and connection counts to decide if an OOS has + * occurred and if so, how many connections to try to kill, and then using + * pick_oos_victims() and kill_conn_list_for_oos() to carry out its grim + * duty. + */ +static void +test_oos_connection_handle_oos(void *arg) +{ + (void)arg; + + /* Set up mocks */ + reset_options_mock(); + /* OOS handling is only sensitive to these fields */ + mock_options.ConnLimit = 32; + mock_options.ConnLimit_ = 64; + mock_options.ConnLimit_high_thresh = 60; + mock_options.ConnLimit_low_thresh = 50; + MOCK(get_options, mock_get_options); + moribund_calls = 0; + moribund_conns = 0; + MOCK(connection_count_moribund, mock_connection_count_moribund); + kill_conn_list_calls = 0; + kill_conn_list_killed = 0; + MOCK(kill_conn_list_for_oos, kill_conn_list_mock); + pick_oos_mock_calls = 0; + pick_oos_mock_fail = 0; + MOCK(pick_oos_victims, pick_oos_victims_mock); + + /* No OOS case */ + connection_handle_oos(50, 0); + tt_int_op(moribund_calls, OP_EQ, 0); + tt_int_op(pick_oos_mock_calls, OP_EQ, 0); + tt_int_op(kill_conn_list_calls, OP_EQ, 0); + + /* OOS from socket count, nothing moribund */ + connection_handle_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 1); + tt_int_op(pick_oos_mock_calls, OP_EQ, 1); + /* 12 == 62 - ConnLimit_low_thresh */ + tt_int_op(pick_oos_mock_last_n, OP_EQ, 12); + tt_int_op(kill_conn_list_calls, OP_EQ, 1); + tt_int_op(kill_conn_list_killed, OP_EQ, 12); + + /* OOS from socket count, some are moribund */ + kill_conn_list_killed = 0; + moribund_conns = 5; + connection_handle_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 2); + tt_int_op(pick_oos_mock_calls, OP_EQ, 2); + /* 7 == 62 - ConnLimit_low_thresh - moribund_conns */ + tt_int_op(pick_oos_mock_last_n, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + tt_int_op(kill_conn_list_killed, OP_EQ, 7); + + /* OOS from socket count, but pick fails */ + kill_conn_list_killed = 0; + moribund_conns = 0; + pick_oos_mock_fail = 1; + connection_handle_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 3); + tt_int_op(pick_oos_mock_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + tt_int_op(kill_conn_list_killed, OP_EQ, 0); + pick_oos_mock_fail = 0; + + /* + * OOS from socket count with so many moribund conns + * we have none to kill. + */ + kill_conn_list_killed = 0; + moribund_conns = 15; + connection_handle_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 4); + tt_int_op(pick_oos_mock_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + + /* + * OOS from socket exhaustion; OOS handler will try to + * kill 1/10 (5) of the connections. + */ + kill_conn_list_killed = 0; + moribund_conns = 0; + connection_handle_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 5); + tt_int_op(pick_oos_mock_calls, OP_EQ, 4); + tt_int_op(kill_conn_list_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_killed, OP_EQ, 5); + + /* OOS from socket exhaustion with moribund conns */ + kill_conn_list_killed = 0; + moribund_conns = 2; + connection_handle_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 6); + tt_int_op(pick_oos_mock_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_calls, OP_EQ, 4); + tt_int_op(kill_conn_list_killed, OP_EQ, 3); + + /* OOS from socket exhaustion with many moribund conns */ + kill_conn_list_killed = 0; + moribund_conns = 7; + connection_handle_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 7); + tt_int_op(pick_oos_mock_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_calls, OP_EQ, 4); + + /* OOS with both socket exhaustion and above-threshold */ + kill_conn_list_killed = 0; + moribund_conns = 0; + connection_handle_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 8); + tt_int_op(pick_oos_mock_calls, OP_EQ, 6); + tt_int_op(kill_conn_list_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_killed, OP_EQ, 12); + + /* + * OOS with both socket exhaustion and above-threshold with some + * moribund conns + */ + kill_conn_list_killed = 0; + moribund_conns = 5; + connection_handle_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 9); + tt_int_op(pick_oos_mock_calls, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 6); + tt_int_op(kill_conn_list_killed, OP_EQ, 7); + + /* + * OOS with both socket exhaustion and above-threshold with many + * moribund conns + */ + kill_conn_list_killed = 0; + moribund_conns = 15; + connection_handle_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 10); + tt_int_op(pick_oos_mock_calls, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 6); + + done: + + UNMOCK(pick_oos_victims); + UNMOCK(kill_conn_list_for_oos); + UNMOCK(connection_count_moribund); + UNMOCK(get_options); + + return; +} + +struct testcase_t oos_tests[] = { + { "connection_handle_oos", test_oos_connection_handle_oos, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + |