diff options
Diffstat (limited to 'src/test')
46 files changed, 3735 insertions, 61 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index f6ee7f3f53..0435617683 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -11,11 +11,12 @@ LIBS = ..\..\..\build-alpha\lib\libevent.lib \ ws2_32.lib advapi32.lib shell32.lib \ crypt32.lib gdi32.lib user32.lib -TEST_OBJECTS = test.obj test_addr.obj test_containers.obj \ +TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \ + test_containers.obj \ test_controller_events.obj test_crypto.obj test_data.obj test_dir.obj \ test_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj test_config.obj \ - test_cell_formats.obj test_replay.obj test_introduce.obj tinytest.obj \ - test_hs.obj + test_cell_formats.obj test_relay.obj test_replay.obj \ + test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj tinytest.obj: ..\ext\tinytest.c $(CC) $(CFLAGS) /D snprintf=_snprintf /c ..\ext\tinytest.c diff --git a/src/test/bench.c b/src/test/bench.c index 74af06c6e6..68870f8657 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Ordinarily defined in tor_main.c; this bit is just here to provide one diff --git a/src/test/bt_test.py b/src/test/bt_test.py index 8290509fa7..0afe797a6d 100755 --- a/src/test/bt_test.py +++ b/src/test/bt_test.py @@ -1,4 +1,4 @@ -# Copyright 2013, The Tor Project, Inc +# Copyright 2013-2015, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/ed25519_exts_ref.py b/src/test/ed25519_exts_ref.py index 93dc49ee93..d5a3a79910 100644 --- a/src/test/ed25519_exts_ref.py +++ b/src/test/ed25519_exts_ref.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2014, The Tor Project, Inc +# Copyright 2014-2015, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/fakechans.h b/src/test/fakechans.h new file mode 100644 index 0000000000..8fb8f420a8 --- /dev/null +++ b/src/test/fakechans.h @@ -0,0 +1,26 @@ + /* Copyright (c) 2014-2015, The Tor Project, Inc. */ + /* See LICENSE for licensing information */ + +#ifndef TOR_FAKECHANS_H +#define TOR_FAKECHANS_H + +/** + * \file fakechans.h + * \brief Declarations for fake channels for test suite use + */ + +void make_fake_cell(cell_t *c); +void make_fake_var_cell(var_cell_t *c); +channel_t * new_fake_channel(void); +void free_fake_channel(channel_t *c); + +/* Also exposes some a mock used by both test_channel.c and test_relay.c */ +void scheduler_channel_has_waiting_cells_mock(channel_t *ch); +void scheduler_release_channel_mock(channel_t *ch); + +/* Query some counters used by the exposed mocks */ +int get_mock_scheduler_has_waiting_cells_count(void); +int get_mock_scheduler_release_channel_count(void); + +#endif /* !defined(TOR_FAKECHANS_H) */ + diff --git a/src/test/include.am b/src/test/include.am index 9abf3094eb..9db1587da7 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -20,6 +20,8 @@ src_test_test_SOURCES = \ src/test/test_addr.c \ src/test/test_buffers.c \ src/test/test_cell_formats.c \ + src/test/test_channel.c \ + src/test/test_channeltls.c \ src/test/test_circuitlist.c \ src/test/test_circuitmux.c \ src/test/test_containers.c \ @@ -39,8 +41,10 @@ src_test_test_SOURCES = \ src/test/test_options.c \ src/test/test_pt.c \ src/test/test_relaycell.c \ + src/test/test_relay.c \ src/test/test_replay.c \ src/test/test_routerkeys.c \ + src/test/test_scheduler.c \ src/test/test_socks.c \ src/test/test_util.c \ src/test/test_config.c \ @@ -64,7 +68,8 @@ src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ src/common/libor-crypto-testing.a $(LIBDONNA) \ src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_SYSTEMD_LIBS@ src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ @@ -72,9 +77,11 @@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ src/common/libor-crypto.a $(LIBDONNA) \ src/common/libor-event.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ - @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_SYSTEMD_LIBS@ noinst_HEADERS+= \ + src/test/fakechans.h \ src/test/test.h \ src/test/test_descriptors.inc \ src/test/example_extrainfo.inc \ diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py index 7d6e43e716..e37637d92a 100755 --- a/src/test/ntor_ref.py +++ b/src/test/ntor_ref.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2012-2013, The Tor Project, Inc +# Copyright 2012-2015, The Tor Project, Inc # See LICENSE for licensing information """ diff --git a/src/test/test-child.c b/src/test/test-child.c index 91ae5a66a5..2ce01ea9bb 100644 --- a/src/test/test-child.c +++ b/src/test/test-child.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2014, The Tor Project, Inc. */ +/* Copyright (c) 2011-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include <stdio.h> diff --git a/src/test/test.c b/src/test/test.c index 032a9d766e..de6efaf873 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Ordinarily defined in tor_main.c; this bit is just here to provide one @@ -1308,6 +1308,11 @@ extern struct testcase_t accounting_tests[]; extern struct testcase_t policy_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t routerset_tests[]; +extern struct testcase_t router_tests[]; +extern struct testcase_t channel_tests[]; +extern struct testcase_t channeltls_tests[]; +extern struct testcase_t relay_tests[]; +extern struct testcase_t scheduler_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -1342,6 +1347,10 @@ static struct testgroup_t testgroups[] = { { "policy/" , policy_tests }, { "status/" , status_tests }, { "routerset/" , routerset_tests }, + { "channel/", channel_tests }, + { "channeltls/", channeltls_tests }, + { "relay/" , relay_tests }, + { "scheduler/", scheduler_tests }, END_OF_GROUPS }; diff --git a/src/test/test.h b/src/test/test.h index 5518ca3f60..48037a5ba3 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2003, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_TEST_H diff --git a/src/test/test_addr.c b/src/test/test_addr.c index c9c47bdaee..2c25c1ef7d 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ADDRESSMAP_PRIVATE diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c index c0c334656d..0fa0cd5c0a 100644 --- a/src/test/test_bt_cl.c +++ b/src/test/test_bt_cl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Tor Project, Inc. */ +/* Copyright (c) 2012-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index cb29ab0a9e..101f448472 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define BUFFERS_PRIVATE @@ -611,7 +611,7 @@ test_buffers_zlib_impl(int finalize_with_nil) int done; buf = buf_new_with_capacity(128); /* will round up */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD); + zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); msg = tor_malloc(512); crypto_rand(msg, 512); @@ -688,7 +688,7 @@ test_buffers_zlib_fin_at_chunk_end(void *arg) tt_uint_op(buf->head->datalen, OP_EQ, headerjunk); tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk); /* Write an empty string, with finalization on. */ - zlib_state = tor_zlib_new(1, ZLIB_METHOD); + zlib_state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); tt_int_op(write_to_buf_zlib(buf, zlib_state, "", 0, 1), OP_EQ, 0); in_len = buf_datalen(buf); diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index e1f6bd71f7..e86dc0934f 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_cell_queue.c b/src/test/test_cell_queue.c index e2fc95ccd6..effd316f34 100644 --- a/src/test/test_cell_queue.c +++ b/src/test/test_cell_queue.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Tor Project, Inc. */ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CIRCUITLIST_PRIVATE diff --git a/src/test/test_channel.c b/src/test/test_channel.c new file mode 100644 index 0000000000..99633a4026 --- /dev/null +++ b/src/test/test_channel.c @@ -0,0 +1,1703 @@ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TOR_CHANNEL_INTERNAL_ +#define CHANNEL_PRIVATE_ +#include "or.h" +#include "channel.h" +/* For channel_note_destroy_not_pending */ +#include "circuitlist.h" +#include "circuitmux.h" +/* For var_cell_free */ +#include "connection_or.h" +/* For packed_cell stuff */ +#define RELAY_PRIVATE +#include "relay.h" +/* For init/free stuff */ +#include "scheduler.h" + +/* Test suite stuff */ +#include "test.h" +#include "fakechans.h" + +/* This comes from channel.c */ +extern uint64_t estimated_total_queue_size; + +static int test_chan_accept_cells = 0; +static int test_chan_fixed_cells_recved = 0; +static int test_chan_var_cells_recved = 0; +static int test_cells_written = 0; +static int test_destroy_not_pending_calls = 0; +static int test_doesnt_want_writes_count = 0; +static int test_dumpstats_calls = 0; +static int test_has_waiting_cells_count = 0; +static double test_overhead_estimate = 1.0f; +static int test_releases_count = 0; +static circuitmux_t *test_target_cmux = NULL; +static unsigned int test_cmux_cells = 0; +static channel_t *dump_statistics_mock_target = NULL; +static int dump_statistics_mock_matches = 0; + +static void chan_test_channel_dump_statistics_mock( + channel_t *chan, int severity); +static int chan_test_channel_flush_from_first_active_circuit_mock( + channel_t *chan, int max); +static unsigned int chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux); +static void channel_note_destroy_not_pending_mock(channel_t *ch, + circid_t circid); +static void chan_test_cell_handler(channel_t *ch, + cell_t *cell); +static const char * chan_test_describe_transport(channel_t *ch); +static void chan_test_dumpstats(channel_t *ch, int severity); +static void chan_test_var_cell_handler(channel_t *ch, + var_cell_t *var_cell); +static void chan_test_close(channel_t *ch); +static void chan_test_error(channel_t *ch); +static void chan_test_finish_close(channel_t *ch); +static const char * chan_test_get_remote_descr(channel_t *ch, int flags); +static int chan_test_is_canonical(channel_t *ch, int req); +static size_t chan_test_num_bytes_queued(channel_t *ch); +static int chan_test_num_cells_writeable(channel_t *ch); +static int chan_test_write_cell(channel_t *ch, cell_t *cell); +static int chan_test_write_packed_cell(channel_t *ch, + packed_cell_t *packed_cell); +static int chan_test_write_var_cell(channel_t *ch, var_cell_t *var_cell); +static void scheduler_channel_doesnt_want_writes_mock(channel_t *ch); + +static void test_channel_dumpstats(void *arg); +static void test_channel_flush(void *arg); +static void test_channel_flushmux(void *arg); +static void test_channel_incoming(void *arg); +static void test_channel_lifecycle(void *arg); +static void test_channel_multi(void *arg); +static void test_channel_queue_size(void *arg); +static void test_channel_write(void *arg); + +static void +channel_note_destroy_not_pending_mock(channel_t *ch, + circid_t circid) +{ + (void)ch; + (void)circid; + + ++test_destroy_not_pending_calls; +} + +static const char * +chan_test_describe_transport(channel_t *ch) +{ + tt_assert(ch != NULL); + + done: + return "Fake channel for unit tests"; +} + +/** + * Mock for channel_dump_statistics(); if the channel matches the + * target, bump a counter - otherwise ignore. + */ + +static void +chan_test_channel_dump_statistics_mock(channel_t *chan, int severity) +{ + tt_assert(chan != NULL); + + (void)severity; + + if (chan != NULL && chan == dump_statistics_mock_target) { + ++dump_statistics_mock_matches; + } + + done: + return; +} + +/** + * If the target cmux is the cmux for chan, make fake cells up to the + * target number of cells and write them to chan. Otherwise, invoke + * the real channel_flush_from_first_active_circuit(). + */ + +static int +chan_test_channel_flush_from_first_active_circuit_mock(channel_t *chan, + int max) +{ + int result = 0, c = 0; + packed_cell_t *cell = NULL; + + tt_assert(chan != NULL); + if (test_target_cmux != NULL && + test_target_cmux == chan->cmux) { + while (c <= max && test_cmux_cells > 0) { + cell = packed_cell_new(); + channel_write_packed_cell(chan, cell); + ++c; + --test_cmux_cells; + } + result = c; + } else { + result = channel_flush_from_first_active_circuit__real(chan, max); + } + + done: + return result; +} + +/** + * If we have a target cmux set and this matches it, lie about how + * many cells we have according to the number indicated; otherwise + * pass to the real circuitmux_num_cells(). + */ + +static unsigned int +chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux) +{ + unsigned int result = 0; + + tt_assert(cmux != NULL); + if (cmux != NULL) { + if (cmux == test_target_cmux) { + result = test_cmux_cells; + } else { + result = circuitmux_num_cells__real(cmux); + } + } + + done: + + return result; +} + +/* + * Handle an incoming fixed-size cell for unit tests + */ + +static void +chan_test_cell_handler(channel_t *ch, + cell_t *cell) +{ + tt_assert(ch); + tt_assert(cell); + + tor_free(cell); + ++test_chan_fixed_cells_recved; + + done: + return; +} + +/* + * Fake transport-specific stats call + */ + +static void +chan_test_dumpstats(channel_t *ch, int severity) +{ + tt_assert(ch != NULL); + + (void)severity; + + ++test_dumpstats_calls; + + done: + return; +} + +/* + * Handle an incoming variable-size cell for unit tests + */ + +static void +chan_test_var_cell_handler(channel_t *ch, + var_cell_t *var_cell) +{ + tt_assert(ch); + tt_assert(var_cell); + + tor_free(var_cell); + ++test_chan_var_cells_recved; + + done: + return; +} + +static void +chan_test_close(channel_t *ch) +{ + tt_assert(ch); + + done: + return; +} + +/* + * Close a channel through the error path + */ + +static void +chan_test_error(channel_t *ch) +{ + tt_assert(ch); + tt_assert(!(ch->state == CHANNEL_STATE_CLOSING || + ch->state == CHANNEL_STATE_ERROR || + ch->state == CHANNEL_STATE_CLOSED)); + + channel_close_for_error(ch); + + done: + return; +} + +/* + * Finish closing a channel from CHANNEL_STATE_CLOSING + */ + +static void +chan_test_finish_close(channel_t *ch) +{ + tt_assert(ch); + tt_assert(ch->state == CHANNEL_STATE_CLOSING); + + channel_closed(ch); + + done: + return; +} + +static const char * +chan_test_get_remote_descr(channel_t *ch, int flags) +{ + tt_assert(ch); + tt_int_op(flags & ~(GRD_FLAG_ORIGINAL | GRD_FLAG_ADDR_ONLY), ==, 0); + + done: + return "Fake channel for unit tests; no real endpoint"; +} + +static double +chan_test_get_overhead_estimate(channel_t *ch) +{ + tt_assert(ch); + + done: + return test_overhead_estimate; +} + +static int +chan_test_is_canonical(channel_t *ch, int req) +{ + tt_assert(ch != NULL); + tt_assert(req == 0 || req == 1); + + done: + /* Fake channels are always canonical */ + return 1; +} + +static size_t +chan_test_num_bytes_queued(channel_t *ch) +{ + tt_assert(ch); + + done: + return 0; +} + +static int +chan_test_num_cells_writeable(channel_t *ch) +{ + tt_assert(ch); + + done: + return 32; +} + +static int +chan_test_write_cell(channel_t *ch, cell_t *cell) +{ + int rv = 0; + + tt_assert(ch); + tt_assert(cell); + + if (test_chan_accept_cells) { + /* Free the cell and bump the counter */ + tor_free(cell); + ++test_cells_written; + rv = 1; + } + /* else return 0, we didn't accept it */ + + done: + return rv; +} + +static int +chan_test_write_packed_cell(channel_t *ch, + packed_cell_t *packed_cell) +{ + int rv = 0; + + tt_assert(ch); + tt_assert(packed_cell); + + if (test_chan_accept_cells) { + /* Free the cell and bump the counter */ + packed_cell_free(packed_cell); + ++test_cells_written; + rv = 1; + } + /* else return 0, we didn't accept it */ + + done: + return rv; +} + +static int +chan_test_write_var_cell(channel_t *ch, var_cell_t *var_cell) +{ + int rv = 0; + + tt_assert(ch); + tt_assert(var_cell); + + if (test_chan_accept_cells) { + /* Free the cell and bump the counter */ + var_cell_free(var_cell); + ++test_cells_written; + rv = 1; + } + /* else return 0, we didn't accept it */ + + done: + return rv; +} + +/** + * Fill out c with a new fake cell for test suite use + */ + +void +make_fake_cell(cell_t *c) +{ + tt_assert(c != NULL); + + c->circ_id = 1; + c->command = CELL_RELAY; + memset(c->payload, 0, CELL_PAYLOAD_SIZE); + + done: + return; +} + +/** + * Fill out c with a new fake var_cell for test suite use + */ + +void +make_fake_var_cell(var_cell_t *c) +{ + tt_assert(c != NULL); + + c->circ_id = 1; + c->command = CELL_VERSIONS; + c->payload_len = CELL_PAYLOAD_SIZE / 2; + memset(c->payload, 0, c->payload_len); + + done: + return; +} + +/** + * Set up a new fake channel for the test suite + */ + +channel_t * +new_fake_channel(void) +{ + channel_t *chan = tor_malloc_zero(sizeof(channel_t)); + channel_init(chan); + + chan->close = chan_test_close; + chan->get_overhead_estimate = chan_test_get_overhead_estimate; + chan->num_bytes_queued = chan_test_num_bytes_queued; + chan->num_cells_writeable = chan_test_num_cells_writeable; + chan->write_cell = chan_test_write_cell; + chan->write_packed_cell = chan_test_write_packed_cell; + chan->write_var_cell = chan_test_write_var_cell; + chan->state = CHANNEL_STATE_OPEN; + + return chan; +} + +void +free_fake_channel(channel_t *chan) +{ + cell_queue_entry_t *cell, *cell_tmp; + + if (! chan) + return; + + if (chan->cmux) + circuitmux_free(chan->cmux); + + TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->incoming_queue, next, cell_tmp) { + cell_queue_entry_free(cell, 0); + } + TOR_SIMPLEQ_FOREACH_SAFE(cell, &chan->outgoing_queue, next, cell_tmp) { + cell_queue_entry_free(cell, 0); + } + + tor_free(chan); +} + +/** + * Counter query for scheduler_channel_has_waiting_cells_mock() + */ + +int +get_mock_scheduler_has_waiting_cells_count(void) +{ + return test_has_waiting_cells_count; +} + +/** + * Mock for scheduler_channel_has_waiting_cells() + */ + +void +scheduler_channel_has_waiting_cells_mock(channel_t *ch) +{ + (void)ch; + + /* Increment counter */ + ++test_has_waiting_cells_count; + + return; +} + +static void +scheduler_channel_doesnt_want_writes_mock(channel_t *ch) +{ + (void)ch; + + /* Increment counter */ + ++test_doesnt_want_writes_count; + + return; +} + +/** + * Counter query for scheduler_release_channel_mock() + */ + +int +get_mock_scheduler_release_channel_count(void) +{ + return test_releases_count; +} + +/** + * Mock for scheduler_release_channel() + */ + +void +scheduler_release_channel_mock(channel_t *ch) +{ + (void)ch; + + /* Increment counter */ + ++test_releases_count; + + return; +} + +/** + * Test for channel_dumpstats() and limited test for + * channel_dump_statistics() + */ + +static void +test_channel_dumpstats(void *arg) +{ + channel_t *ch = NULL; + cell_t *cell = NULL; + int old_count; + + (void)arg; + + /* Mock these for duration of the test */ + MOCK(scheduler_channel_doesnt_want_writes, + scheduler_channel_doesnt_want_writes_mock); + MOCK(scheduler_release_channel, + scheduler_release_channel_mock); + + /* Set up a new fake channel */ + ch = new_fake_channel(); + tt_assert(ch); + ch->cmux = circuitmux_alloc(); + + /* Try to register it */ + channel_register(ch); + tt_assert(ch->registered); + + /* Set up mock */ + dump_statistics_mock_target = ch; + dump_statistics_mock_matches = 0; + MOCK(channel_dump_statistics, + chan_test_channel_dump_statistics_mock); + + /* Call channel_dumpstats() */ + channel_dumpstats(LOG_DEBUG); + + /* Assert that we hit the mock */ + tt_int_op(dump_statistics_mock_matches, ==, 1); + + /* Close the channel */ + channel_mark_for_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED); + + /* Try again and hit the finished channel */ + channel_dumpstats(LOG_DEBUG); + tt_int_op(dump_statistics_mock_matches, ==, 2); + + channel_run_cleanup(); + ch = NULL; + + /* Now we should hit nothing */ + channel_dumpstats(LOG_DEBUG); + tt_int_op(dump_statistics_mock_matches, ==, 2); + + /* Unmock */ + UNMOCK(channel_dump_statistics); + dump_statistics_mock_target = NULL; + dump_statistics_mock_matches = 0; + + /* Now make another channel */ + ch = new_fake_channel(); + tt_assert(ch); + ch->cmux = circuitmux_alloc(); + channel_register(ch); + tt_assert(ch->registered); + /* Lie about its age so dumpstats gets coverage for rate calculations */ + ch->timestamp_created = time(NULL) - 30; + tt_assert(ch->timestamp_created > 0); + tt_assert(time(NULL) > ch->timestamp_created); + + /* Put cells through it both ways to make the counters non-zero */ + cell = tor_malloc_zero(sizeof(*cell)); + make_fake_cell(cell); + test_chan_accept_cells = 1; + old_count = test_cells_written; + channel_write_cell(ch, cell); + cell = NULL; + tt_int_op(test_cells_written, ==, old_count + 1); + tt_assert(ch->n_bytes_xmitted > 0); + tt_assert(ch->n_cells_xmitted > 0); + + /* Receive path */ + channel_set_cell_handlers(ch, + chan_test_cell_handler, + chan_test_var_cell_handler); + tt_ptr_op(channel_get_cell_handler(ch), ==, chan_test_cell_handler); + tt_ptr_op(channel_get_var_cell_handler(ch), ==, chan_test_var_cell_handler); + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + old_count = test_chan_fixed_cells_recved; + channel_queue_cell(ch, cell); + cell = NULL; + tt_int_op(test_chan_fixed_cells_recved, ==, old_count + 1); + tt_assert(ch->n_bytes_recved > 0); + tt_assert(ch->n_cells_recved > 0); + + /* Test channel_dump_statistics */ + ch->describe_transport = chan_test_describe_transport; + ch->dumpstats = chan_test_dumpstats; + ch->get_remote_descr = chan_test_get_remote_descr; + ch->is_canonical = chan_test_is_canonical; + old_count = test_dumpstats_calls; + channel_dump_statistics(ch, LOG_DEBUG); + tt_int_op(test_dumpstats_calls, ==, old_count + 1); + + /* Close the channel */ + channel_mark_for_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED); + channel_run_cleanup(); + ch = NULL; + + done: + tor_free(cell); + free_fake_channel(ch); + + UNMOCK(scheduler_channel_doesnt_want_writes); + UNMOCK(scheduler_release_channel); + + return; +} + +static void +test_channel_flush(void *arg) +{ + channel_t *ch = NULL; + cell_t *cell = NULL; + packed_cell_t *p_cell = NULL; + var_cell_t *v_cell = NULL; + int init_count; + + (void)arg; + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + ch = new_fake_channel(); + tt_assert(ch); + + /* Cache the original count */ + init_count = test_cells_written; + + /* Stop accepting so we can queue some */ + test_chan_accept_cells = 0; + + /* Queue a regular cell */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch, cell); + /* It should be queued, so assert that we didn't write it */ + tt_int_op(test_cells_written, ==, init_count); + + /* Queue a var cell */ + v_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); + make_fake_var_cell(v_cell); + channel_write_var_cell(ch, v_cell); + /* It should be queued, so assert that we didn't write it */ + tt_int_op(test_cells_written, ==, init_count); + + /* Try a packed cell now */ + p_cell = packed_cell_new(); + tt_assert(p_cell); + channel_write_packed_cell(ch, p_cell); + /* It should be queued, so assert that we didn't write it */ + tt_int_op(test_cells_written, ==, init_count); + + /* Now allow writes through again */ + test_chan_accept_cells = 1; + + /* ...and flush */ + channel_flush_cells(ch); + + /* All three should have gone through */ + tt_int_op(test_cells_written, ==, init_count + 3); + + done: + tor_free(ch); +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + return; +} + +/** + * Channel flush tests that require cmux mocking + */ + +static void +test_channel_flushmux(void *arg) +{ + channel_t *ch = NULL; + int old_count, q_len_before, q_len_after; + ssize_t result; + + (void)arg; + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + /* Install mocks we need for this test */ + MOCK(channel_flush_from_first_active_circuit, + chan_test_channel_flush_from_first_active_circuit_mock); + MOCK(circuitmux_num_cells, + chan_test_circuitmux_num_cells_mock); + + ch = new_fake_channel(); + tt_assert(ch); + ch->cmux = circuitmux_alloc(); + + old_count = test_cells_written; + + test_target_cmux = ch->cmux; + test_cmux_cells = 1; + + /* Enable cell acceptance */ + test_chan_accept_cells = 1; + + result = channel_flush_some_cells(ch, 1); + + tt_int_op(result, ==, 1); + tt_int_op(test_cells_written, ==, old_count + 1); + tt_int_op(test_cmux_cells, ==, 0); + + /* Now try it without accepting to force them into the queue */ + test_chan_accept_cells = 0; + test_cmux_cells = 1; + q_len_before = chan_cell_queue_len(&(ch->outgoing_queue)); + + result = channel_flush_some_cells(ch, 1); + + /* We should not have actually flushed any */ + tt_int_op(result, ==, 0); + tt_int_op(test_cells_written, ==, old_count + 1); + /* But we should have gotten to the fake cellgen loop */ + tt_int_op(test_cmux_cells, ==, 0); + /* ...and we should have a queued cell */ + q_len_after = chan_cell_queue_len(&(ch->outgoing_queue)); + tt_int_op(q_len_after, ==, q_len_before + 1); + + /* Now accept cells again and drain the queue */ + test_chan_accept_cells = 1; + channel_flush_cells(ch); + tt_int_op(test_cells_written, ==, old_count + 2); + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + + test_target_cmux = NULL; + test_cmux_cells = 0; + + done: + if (ch) + circuitmux_free(ch->cmux); + tor_free(ch); + + UNMOCK(channel_flush_from_first_active_circuit); + UNMOCK(circuitmux_num_cells); + + test_chan_accept_cells = 0; + +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + return; +} + +static void +test_channel_incoming(void *arg) +{ + channel_t *ch = NULL; + cell_t *cell = NULL; + var_cell_t *var_cell = NULL; + int old_count; + + (void)arg; + + /* Mock these for duration of the test */ + MOCK(scheduler_channel_doesnt_want_writes, + scheduler_channel_doesnt_want_writes_mock); + MOCK(scheduler_release_channel, + scheduler_release_channel_mock); + + /* Accept cells to lower layer */ + test_chan_accept_cells = 1; + /* Use default overhead factor */ + test_overhead_estimate = 1.0f; + + ch = new_fake_channel(); + tt_assert(ch); + /* Start it off in OPENING */ + ch->state = CHANNEL_STATE_OPENING; + /* We'll need a cmux */ + ch->cmux = circuitmux_alloc(); + + /* Install incoming cell handlers */ + channel_set_cell_handlers(ch, + chan_test_cell_handler, + chan_test_var_cell_handler); + /* Test cell handler getters */ + tt_ptr_op(channel_get_cell_handler(ch), ==, chan_test_cell_handler); + tt_ptr_op(channel_get_var_cell_handler(ch), ==, chan_test_var_cell_handler); + + /* Try to register it */ + channel_register(ch); + tt_assert(ch->registered); + + /* Open it */ + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN); + + /* Receive a fixed cell */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + old_count = test_chan_fixed_cells_recved; + channel_queue_cell(ch, cell); + cell = NULL; + tt_int_op(test_chan_fixed_cells_recved, ==, old_count + 1); + + /* Receive a variable-size cell */ + var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); + make_fake_var_cell(var_cell); + old_count = test_chan_var_cells_recved; + channel_queue_var_cell(ch, var_cell); + var_cell = NULL; + tt_int_op(test_chan_var_cells_recved, ==, old_count + 1); + + /* Close it */ + channel_mark_for_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED); + channel_run_cleanup(); + ch = NULL; + + done: + free_fake_channel(ch); + tor_free(cell); + tor_free(var_cell); + + UNMOCK(scheduler_channel_doesnt_want_writes); + UNMOCK(scheduler_release_channel); + + return; +} + +/** + * Normal channel lifecycle test: + * + * OPENING->OPEN->MAINT->OPEN->CLOSING->CLOSED + */ + +static void +test_channel_lifecycle(void *arg) +{ + channel_t *ch1 = NULL, *ch2 = NULL; + cell_t *cell = NULL; + int old_count, init_doesnt_want_writes_count; + int init_releases_count; + + (void)arg; + + /* Mock these for the whole lifecycle test */ + MOCK(scheduler_channel_doesnt_want_writes, + scheduler_channel_doesnt_want_writes_mock); + MOCK(scheduler_release_channel, + scheduler_release_channel_mock); + + /* Cache some initial counter values */ + init_doesnt_want_writes_count = test_doesnt_want_writes_count; + init_releases_count = test_releases_count; + + /* Accept cells to lower layer */ + test_chan_accept_cells = 1; + /* Use default overhead factor */ + test_overhead_estimate = 1.0f; + + ch1 = new_fake_channel(); + tt_assert(ch1); + /* Start it off in OPENING */ + ch1->state = CHANNEL_STATE_OPENING; + /* We'll need a cmux */ + ch1->cmux = circuitmux_alloc(); + + /* Try to register it */ + channel_register(ch1); + tt_assert(ch1->registered); + + /* Try to write a cell through (should queue) */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + old_count = test_cells_written; + channel_write_cell(ch1, cell); + tt_int_op(old_count, ==, test_cells_written); + + /* Move it to OPEN and flush */ + channel_change_state(ch1, CHANNEL_STATE_OPEN); + + /* Queue should drain */ + tt_int_op(old_count + 1, ==, test_cells_written); + + /* Get another one */ + ch2 = new_fake_channel(); + tt_assert(ch2); + ch2->state = CHANNEL_STATE_OPENING; + ch2->cmux = circuitmux_alloc(); + + /* Register */ + channel_register(ch2); + tt_assert(ch2->registered); + + /* Check counters */ + tt_int_op(test_doesnt_want_writes_count, ==, init_doesnt_want_writes_count); + tt_int_op(test_releases_count, ==, init_releases_count); + + /* Move ch1 to MAINT */ + channel_change_state(ch1, CHANNEL_STATE_MAINT); + tt_int_op(test_doesnt_want_writes_count, ==, + init_doesnt_want_writes_count + 1); + tt_int_op(test_releases_count, ==, init_releases_count); + + /* Move ch2 to OPEN */ + channel_change_state(ch2, CHANNEL_STATE_OPEN); + tt_int_op(test_doesnt_want_writes_count, ==, + init_doesnt_want_writes_count + 1); + tt_int_op(test_releases_count, ==, init_releases_count); + + /* Move ch1 back to OPEN */ + channel_change_state(ch1, CHANNEL_STATE_OPEN); + tt_int_op(test_doesnt_want_writes_count, ==, + init_doesnt_want_writes_count + 1); + tt_int_op(test_releases_count, ==, init_releases_count); + + /* Mark ch2 for close */ + channel_mark_for_close(ch2); + tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSING); + tt_int_op(test_doesnt_want_writes_count, ==, + init_doesnt_want_writes_count + 1); + tt_int_op(test_releases_count, ==, init_releases_count + 1); + + /* Shut down channels */ + channel_free_all(); + ch1 = ch2 = NULL; + tt_int_op(test_doesnt_want_writes_count, ==, + init_doesnt_want_writes_count + 1); + /* channel_free() calls scheduler_release_channel() */ + tt_int_op(test_releases_count, ==, init_releases_count + 4); + + done: + free_fake_channel(ch1); + free_fake_channel(ch2); + + UNMOCK(scheduler_channel_doesnt_want_writes); + UNMOCK(scheduler_release_channel); + + return; +} + +/** + * Weird channel lifecycle test: + * + * OPENING->CLOSING->CLOSED + * OPENING->OPEN->CLOSING->ERROR + * OPENING->OPEN->MAINT->CLOSING->CLOSED + * OPENING->OPEN->MAINT->CLOSING->ERROR + */ + +static void +test_channel_lifecycle_2(void *arg) +{ + channel_t *ch = NULL; + + (void)arg; + + /* Mock these for the whole lifecycle test */ + MOCK(scheduler_channel_doesnt_want_writes, + scheduler_channel_doesnt_want_writes_mock); + MOCK(scheduler_release_channel, + scheduler_release_channel_mock); + + /* Accept cells to lower layer */ + test_chan_accept_cells = 1; + /* Use default overhead factor */ + test_overhead_estimate = 1.0f; + + ch = new_fake_channel(); + tt_assert(ch); + /* Start it off in OPENING */ + ch->state = CHANNEL_STATE_OPENING; + /* The full lifecycle test needs a cmux */ + ch->cmux = circuitmux_alloc(); + + /* Try to register it */ + channel_register(ch); + tt_assert(ch->registered); + + /* Try to close it */ + channel_mark_for_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + + /* Finish closing it */ + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED); + channel_run_cleanup(); + ch = NULL; + + /* Now try OPENING->OPEN->CLOSING->ERROR */ + ch = new_fake_channel(); + tt_assert(ch); + ch->state = CHANNEL_STATE_OPENING; + ch->cmux = circuitmux_alloc(); + channel_register(ch); + tt_assert(ch->registered); + + /* Finish opening it */ + channel_change_state(ch, CHANNEL_STATE_OPEN); + + /* Error exit from lower layer */ + chan_test_error(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_ERROR); + channel_run_cleanup(); + ch = NULL; + + /* OPENING->OPEN->MAINT->CLOSING->CLOSED close from maintenance state */ + ch = new_fake_channel(); + tt_assert(ch); + ch->state = CHANNEL_STATE_OPENING; + ch->cmux = circuitmux_alloc(); + channel_register(ch); + tt_assert(ch->registered); + + /* Finish opening it */ + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN); + + /* Go to maintenance state */ + channel_change_state(ch, CHANNEL_STATE_MAINT); + tt_int_op(ch->state, ==, CHANNEL_STATE_MAINT); + + /* Lower layer close */ + channel_mark_for_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + + /* Finish */ + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED); + channel_run_cleanup(); + ch = NULL; + + /* + * OPENING->OPEN->MAINT->CLOSING->CLOSED lower-layer close during + * maintenance state + */ + ch = new_fake_channel(); + tt_assert(ch); + ch->state = CHANNEL_STATE_OPENING; + ch->cmux = circuitmux_alloc(); + channel_register(ch); + tt_assert(ch->registered); + + /* Finish opening it */ + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN); + + /* Go to maintenance state */ + channel_change_state(ch, CHANNEL_STATE_MAINT); + tt_int_op(ch->state, ==, CHANNEL_STATE_MAINT); + + /* Lower layer close */ + channel_close_from_lower_layer(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + + /* Finish */ + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED); + channel_run_cleanup(); + ch = NULL; + + /* OPENING->OPEN->MAINT->CLOSING->ERROR */ + ch = new_fake_channel(); + tt_assert(ch); + ch->state = CHANNEL_STATE_OPENING; + ch->cmux = circuitmux_alloc(); + channel_register(ch); + tt_assert(ch->registered); + + /* Finish opening it */ + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN); + + /* Go to maintenance state */ + channel_change_state(ch, CHANNEL_STATE_MAINT); + tt_int_op(ch->state, ==, CHANNEL_STATE_MAINT); + + /* Lower layer close */ + chan_test_error(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING); + + /* Finish */ + chan_test_finish_close(ch); + tt_int_op(ch->state, ==, CHANNEL_STATE_ERROR); + channel_run_cleanup(); + ch = NULL; + + /* Shut down channels */ + channel_free_all(); + + done: + tor_free(ch); + + UNMOCK(scheduler_channel_doesnt_want_writes); + UNMOCK(scheduler_release_channel); + + return; +} + +static void +test_channel_multi(void *arg) +{ + channel_t *ch1 = NULL, *ch2 = NULL; + uint64_t global_queue_estimate; + cell_t *cell = NULL; + + (void)arg; + + /* Accept cells to lower layer */ + test_chan_accept_cells = 1; + /* Use default overhead factor */ + test_overhead_estimate = 1.0f; + + ch1 = new_fake_channel(); + tt_assert(ch1); + ch2 = new_fake_channel(); + tt_assert(ch2); + + /* Initial queue size update */ + channel_update_xmit_queue_size(ch1); + tt_u64_op(ch1->bytes_queued_for_xmit, ==, 0); + channel_update_xmit_queue_size(ch2); + tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 0); + + /* Queue some cells, check queue estimates */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch1, cell); + + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch2, cell); + + channel_update_xmit_queue_size(ch1); + channel_update_xmit_queue_size(ch2); + tt_u64_op(ch1->bytes_queued_for_xmit, ==, 0); + tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 0); + + /* Stop accepting cells at lower layer */ + test_chan_accept_cells = 0; + + /* Queue some cells and check queue estimates */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch1, cell); + + channel_update_xmit_queue_size(ch1); + tt_u64_op(ch1->bytes_queued_for_xmit, ==, 512); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 512); + + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch2, cell); + + channel_update_xmit_queue_size(ch2); + tt_u64_op(ch2->bytes_queued_for_xmit, ==, 512); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 1024); + + /* Allow cells through again */ + test_chan_accept_cells = 1; + + /* Flush chan 2 */ + channel_flush_cells(ch2); + + /* Update and check queue sizes */ + channel_update_xmit_queue_size(ch1); + channel_update_xmit_queue_size(ch2); + tt_u64_op(ch1->bytes_queued_for_xmit, ==, 512); + tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 512); + + /* Flush chan 1 */ + channel_flush_cells(ch1); + + /* Update and check queue sizes */ + channel_update_xmit_queue_size(ch1); + channel_update_xmit_queue_size(ch2); + tt_u64_op(ch1->bytes_queued_for_xmit, ==, 0); + tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 0); + + /* Now block again */ + test_chan_accept_cells = 0; + + /* Queue some cells */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch1, cell); + + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch2, cell); + cell = NULL; + + /* Check the estimates */ + channel_update_xmit_queue_size(ch1); + channel_update_xmit_queue_size(ch2); + tt_u64_op(ch1->bytes_queued_for_xmit, ==, 512); + tt_u64_op(ch2->bytes_queued_for_xmit, ==, 512); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 1024); + + /* Now close channel 2; it should be subtracted from the global queue */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + channel_mark_for_close(ch2); + UNMOCK(scheduler_release_channel); + + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 512); + + /* + * Since the fake channels aren't registered, channel_free_all() can't + * see them properly. + */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + channel_mark_for_close(ch1); + UNMOCK(scheduler_release_channel); + + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 0); + + /* Now free everything */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + channel_free_all(); + UNMOCK(scheduler_release_channel); + + done: + free_fake_channel(ch1); + free_fake_channel(ch2); + + return; +} + +/** + * Check some hopefully-impossible edge cases in the channel queue we + * can only trigger by doing evil things to the queue directly. + */ + +static void +test_channel_queue_impossible(void *arg) +{ + channel_t *ch = NULL; + cell_t *cell = NULL; + packed_cell_t *packed_cell = NULL; + var_cell_t *var_cell = NULL; + int old_count; + cell_queue_entry_t *q = NULL; + uint64_t global_queue_estimate; + uintptr_t cellintptr; + + /* Cache the global queue size (see below) */ + global_queue_estimate = channel_get_global_queue_estimate(); + + (void)arg; + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + ch = new_fake_channel(); + tt_assert(ch); + + /* We test queueing here; tell it not to accept cells */ + test_chan_accept_cells = 0; + /* ...and keep it from trying to flush the queue */ + ch->state = CHANNEL_STATE_MAINT; + + /* Cache the cell written count */ + old_count = test_cells_written; + + /* Assert that the queue is initially empty */ + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + + /* Get a fresh cell and write it to the channel*/ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + cellintptr = (uintptr_t)(void*)cell; + channel_write_cell(ch, cell); + + /* Now it should be queued */ + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1); + q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); + tt_assert(q); + if (q) { + tt_int_op(q->type, ==, CELL_QUEUE_FIXED); + tt_assert((uintptr_t)q->u.fixed.cell == cellintptr); + } + /* Do perverse things to it */ + tor_free(q->u.fixed.cell); + q->u.fixed.cell = NULL; + + /* + * Now change back to open with channel_change_state() and assert that it + * gets thrown away properly. + */ + test_chan_accept_cells = 1; + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_assert(test_cells_written == old_count); + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + + /* Same thing but for a var_cell */ + + test_chan_accept_cells = 0; + ch->state = CHANNEL_STATE_MAINT; + var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); + make_fake_var_cell(var_cell); + cellintptr = (uintptr_t)(void*)var_cell; + channel_write_var_cell(ch, var_cell); + + /* Check that it's queued */ + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1); + q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); + tt_assert(q); + if (q) { + tt_int_op(q->type, ==, CELL_QUEUE_VAR); + tt_assert((uintptr_t)q->u.var.var_cell == cellintptr); + } + + /* Remove the cell from the queue entry */ + tor_free(q->u.var.var_cell); + q->u.var.var_cell = NULL; + + /* Let it drain and check that the bad entry is discarded */ + test_chan_accept_cells = 1; + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_assert(test_cells_written == old_count); + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + + /* Same thing with a packed_cell */ + + test_chan_accept_cells = 0; + ch->state = CHANNEL_STATE_MAINT; + packed_cell = packed_cell_new(); + tt_assert(packed_cell); + cellintptr = (uintptr_t)(void*)packed_cell; + channel_write_packed_cell(ch, packed_cell); + + /* Check that it's queued */ + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1); + q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); + tt_assert(q); + if (q) { + tt_int_op(q->type, ==, CELL_QUEUE_PACKED); + tt_assert((uintptr_t)q->u.packed.packed_cell == cellintptr); + } + + /* Remove the cell from the queue entry */ + packed_cell_free(q->u.packed.packed_cell); + q->u.packed.packed_cell = NULL; + + /* Let it drain and check that the bad entry is discarded */ + test_chan_accept_cells = 1; + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_assert(test_cells_written == old_count); + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + + /* Unknown cell type case */ + test_chan_accept_cells = 0; + ch->state = CHANNEL_STATE_MAINT; + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + cellintptr = (uintptr_t)(void*)cell; + channel_write_cell(ch, cell); + + /* Check that it's queued */ + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1); + q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue)); + tt_assert(q); + if (q) { + tt_int_op(q->type, ==, CELL_QUEUE_FIXED); + tt_assert((uintptr_t)q->u.fixed.cell == cellintptr); + } + /* Clobber it, including the queue entry type */ + tor_free(q->u.fixed.cell); + q->u.fixed.cell = NULL; + q->type = CELL_QUEUE_PACKED + 1; + + /* Let it drain and check that the bad entry is discarded */ + test_chan_accept_cells = 1; + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_assert(test_cells_written == old_count); + tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + + done: + free_fake_channel(ch); +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + /* + * Doing that meant that we couldn't correctly adjust the queue size + * for the var cell, so manually reset the global queue size estimate + * so the next test doesn't break if we run with --no-fork. + */ + estimated_total_queue_size = global_queue_estimate; + + return; +} + +static void +test_channel_queue_size(void *arg) +{ + channel_t *ch = NULL; + cell_t *cell = NULL; + int n, old_count; + uint64_t global_queue_estimate; + + (void)arg; + + ch = new_fake_channel(); + tt_assert(ch); + + /* Initial queue size update */ + channel_update_xmit_queue_size(ch); + tt_u64_op(ch->bytes_queued_for_xmit, ==, 0); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 0); + + /* Test the call-through to our fake lower layer */ + n = channel_num_cells_writeable(ch); + /* chan_test_num_cells_writeable() always returns 32 */ + tt_int_op(n, ==, 32); + + /* + * Now we queue some cells and check that channel_num_cells_writeable() + * adjusts properly + */ + + /* tell it not to accept cells */ + test_chan_accept_cells = 0; + /* ...and keep it from trying to flush the queue */ + ch->state = CHANNEL_STATE_MAINT; + + /* Get a fresh cell */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + + old_count = test_cells_written; + channel_write_cell(ch, cell); + /* Assert that it got queued, not written through, correctly */ + tt_int_op(test_cells_written, ==, old_count); + + /* Now check chan_test_num_cells_writeable() again */ + n = channel_num_cells_writeable(ch); + tt_int_op(n, ==, 0); /* Should return 0 since we're in CHANNEL_STATE_MAINT */ + + /* Update queue size estimates */ + channel_update_xmit_queue_size(ch); + /* One cell, times an overhead factor of 1.0 */ + tt_u64_op(ch->bytes_queued_for_xmit, ==, 512); + /* Try a different overhead factor */ + test_overhead_estimate = 0.5f; + /* This one should be ignored since it's below 1.0 */ + channel_update_xmit_queue_size(ch); + tt_u64_op(ch->bytes_queued_for_xmit, ==, 512); + /* Now try a larger one */ + test_overhead_estimate = 2.0f; + channel_update_xmit_queue_size(ch); + tt_u64_op(ch->bytes_queued_for_xmit, ==, 1024); + /* Go back to 1.0 */ + test_overhead_estimate = 1.0f; + channel_update_xmit_queue_size(ch); + tt_u64_op(ch->bytes_queued_for_xmit, ==, 512); + /* Check the global estimate too */ + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 512); + + /* Go to open */ + old_count = test_cells_written; + channel_change_state(ch, CHANNEL_STATE_OPEN); + + /* + * It should try to write, but we aren't accepting cells right now, so + * it'll requeue + */ + tt_int_op(test_cells_written, ==, old_count); + + /* Check the queue size again */ + channel_update_xmit_queue_size(ch); + tt_u64_op(ch->bytes_queued_for_xmit, ==, 512); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 512); + + /* + * Now the cell is in the queue, and we're open, so we should get 31 + * writeable cells. + */ + n = channel_num_cells_writeable(ch); + tt_int_op(n, ==, 31); + + /* Accept cells again */ + test_chan_accept_cells = 1; + /* ...and re-process the queue */ + old_count = test_cells_written; + channel_flush_cells(ch); + tt_int_op(test_cells_written, ==, old_count + 1); + + /* Should have 32 writeable now */ + n = channel_num_cells_writeable(ch); + tt_int_op(n, ==, 32); + + /* Should have queue size estimate of zero */ + channel_update_xmit_queue_size(ch); + tt_u64_op(ch->bytes_queued_for_xmit, ==, 0); + global_queue_estimate = channel_get_global_queue_estimate(); + tt_u64_op(global_queue_estimate, ==, 0); + + /* Okay, now we're done with this one */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + channel_mark_for_close(ch); + UNMOCK(scheduler_release_channel); + + done: + free_fake_channel(ch); + + return; +} + +static void +test_channel_write(void *arg) +{ + channel_t *ch = NULL; + cell_t *cell = tor_malloc_zero(sizeof(cell_t)); + packed_cell_t *packed_cell = NULL; + var_cell_t *var_cell = + tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); + int old_count; + + (void)arg; + +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + packed_cell = packed_cell_new(); + tt_assert(packed_cell); + + ch = new_fake_channel(); + tt_assert(ch); + make_fake_cell(cell); + make_fake_var_cell(var_cell); + + /* Tell it to accept cells */ + test_chan_accept_cells = 1; + + old_count = test_cells_written; + channel_write_cell(ch, cell); + cell = NULL; + tt_assert(test_cells_written == old_count + 1); + + channel_write_var_cell(ch, var_cell); + var_cell = NULL; + tt_assert(test_cells_written == old_count + 2); + + channel_write_packed_cell(ch, packed_cell); + packed_cell = NULL; + tt_assert(test_cells_written == old_count + 3); + + /* Now we test queueing; tell it not to accept cells */ + test_chan_accept_cells = 0; + /* ...and keep it from trying to flush the queue */ + ch->state = CHANNEL_STATE_MAINT; + + /* Get a fresh cell */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + + old_count = test_cells_written; + channel_write_cell(ch, cell); + tt_assert(test_cells_written == old_count); + + /* + * Now change back to open with channel_change_state() and assert that it + * gets drained from the queue. + */ + test_chan_accept_cells = 1; + channel_change_state(ch, CHANNEL_STATE_OPEN); + tt_assert(test_cells_written == old_count + 1); + + /* + * Check the note destroy case + */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + cell->command = CELL_DESTROY; + + /* Set up the mock */ + MOCK(channel_note_destroy_not_pending, + channel_note_destroy_not_pending_mock); + + old_count = test_destroy_not_pending_calls; + channel_write_cell(ch, cell); + tt_assert(test_destroy_not_pending_calls == old_count + 1); + + /* Now send a non-destroy and check we don't call it */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch, cell); + tt_assert(test_destroy_not_pending_calls == old_count + 1); + + UNMOCK(channel_note_destroy_not_pending); + + /* + * Now switch it to CLOSING so we can test the discard-cells case + * in the channel_write_*() functions. + */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + channel_mark_for_close(ch); + UNMOCK(scheduler_release_channel); + + /* Send cells that will drop in the closing state */ + old_count = test_cells_written; + + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + channel_write_cell(ch, cell); + cell = NULL; + tt_assert(test_cells_written == old_count); + + var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE); + make_fake_var_cell(var_cell); + channel_write_var_cell(ch, var_cell); + var_cell = NULL; + tt_assert(test_cells_written == old_count); + + packed_cell = packed_cell_new(); + channel_write_packed_cell(ch, packed_cell); + packed_cell = NULL; + tt_assert(test_cells_written == old_count); + +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + done: + free_fake_channel(ch); + tor_free(var_cell); + tor_free(cell); + packed_cell_free(packed_cell); + return; +} + +struct testcase_t channel_tests[] = { + { "dumpstats", test_channel_dumpstats, TT_FORK, NULL, NULL }, + { "flush", test_channel_flush, TT_FORK, NULL, NULL }, + { "flushmux", test_channel_flushmux, TT_FORK, NULL, NULL }, + { "incoming", test_channel_incoming, TT_FORK, NULL, NULL }, + { "lifecycle", test_channel_lifecycle, TT_FORK, NULL, NULL }, + { "lifecycle_2", test_channel_lifecycle_2, TT_FORK, NULL, NULL }, + { "multi", test_channel_multi, TT_FORK, NULL, NULL }, + { "queue_impossible", test_channel_queue_impossible, TT_FORK, NULL, NULL }, + { "queue_size", test_channel_queue_size, TT_FORK, NULL, NULL }, + { "write", test_channel_write, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c new file mode 100644 index 0000000000..016e504ab3 --- /dev/null +++ b/src/test/test_channeltls.c @@ -0,0 +1,333 @@ +/* Copyright (c) 2014-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include <math.h> + +#define TOR_CHANNEL_INTERNAL_ +#include "or.h" +#include "address.h" +#include "buffers.h" +#include "channel.h" +#include "channeltls.h" +#include "connection_or.h" +#include "config.h" +/* For init/free stuff */ +#include "scheduler.h" +#include "tortls.h" + +/* Test suite stuff */ +#include "test.h" +#include "fakechans.h" + +/* The channeltls unit tests */ +static void test_channeltls_create(void *arg); +static void test_channeltls_num_bytes_queued(void *arg); +static void test_channeltls_overhead_estimate(void *arg); + +/* Mocks used by channeltls unit tests */ +static size_t tlschan_buf_datalen_mock(const buf_t *buf); +static or_connection_t * tlschan_connection_or_connect_mock( + const tor_addr_t *addr, + uint16_t port, + const char *digest, + channel_tls_t *tlschan); +static int tlschan_is_local_addr_mock(const tor_addr_t *addr); + +/* Fake close method */ +static void tlschan_fake_close_method(channel_t *chan); + +/* Flags controlling behavior of channeltls unit test mocks */ +static int tlschan_local = 0; +static const buf_t * tlschan_buf_datalen_mock_target = NULL; +static size_t tlschan_buf_datalen_mock_size = 0; + +/* Thing to cast to fake tor_tls_t * to appease assert_connection_ok() */ +static int fake_tortls = 0; /* Bleh... */ + +static void +test_channeltls_create(void *arg) +{ + tor_addr_t test_addr; + channel_t *ch = NULL; + const char test_digest[DIGEST_LEN] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 }; + + (void)arg; + + /* Set up a fake address to fake-connect to */ + test_addr.family = AF_INET; + test_addr.addr.in_addr.s_addr = htonl(0x01020304); + + /* For this test we always want the address to be treated as non-local */ + tlschan_local = 0; + /* Install is_local_addr() mock */ + MOCK(is_local_addr, tlschan_is_local_addr_mock); + + /* Install mock for connection_or_connect() */ + MOCK(connection_or_connect, tlschan_connection_or_connect_mock); + + /* Try connecting */ + ch = channel_tls_connect(&test_addr, 567, test_digest); + tt_assert(ch != NULL); + + done: + if (ch) { + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + /* + * Use fake close method that doesn't try to do too much to fake + * orconn + */ + ch->close = tlschan_fake_close_method; + channel_mark_for_close(ch); + free_fake_channel(ch); + UNMOCK(scheduler_release_channel); + } + + UNMOCK(connection_or_connect); + UNMOCK(is_local_addr); + + return; +} + +static void +test_channeltls_num_bytes_queued(void *arg) +{ + tor_addr_t test_addr; + channel_t *ch = NULL; + const char test_digest[DIGEST_LEN] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 }; + channel_tls_t *tlschan = NULL; + size_t len; + int fake_outbuf = 0, n; + + (void)arg; + + /* Set up a fake address to fake-connect to */ + test_addr.family = AF_INET; + test_addr.addr.in_addr.s_addr = htonl(0x01020304); + + /* For this test we always want the address to be treated as non-local */ + tlschan_local = 0; + /* Install is_local_addr() mock */ + MOCK(is_local_addr, tlschan_is_local_addr_mock); + + /* Install mock for connection_or_connect() */ + MOCK(connection_or_connect, tlschan_connection_or_connect_mock); + + /* Try connecting */ + ch = channel_tls_connect(&test_addr, 567, test_digest); + tt_assert(ch != NULL); + + /* + * Next, we have to test ch->num_bytes_queued, which is + * channel_tls_num_bytes_queued_method. We can't mock + * connection_get_outbuf_len() directly because it's static INLINE + * in connection.h, but we can mock buf_datalen(). Note that + * if bufferevents ever work, this will break with them enabled. + */ + + tt_assert(ch->num_bytes_queued != NULL); + tlschan = BASE_CHAN_TO_TLS(ch); + tt_assert(tlschan != NULL); + if (TO_CONN(tlschan->conn)->outbuf == NULL) { + /* We need an outbuf to make sure buf_datalen() gets called */ + fake_outbuf = 1; + TO_CONN(tlschan->conn)->outbuf = buf_new(); + } + tlschan_buf_datalen_mock_target = TO_CONN(tlschan->conn)->outbuf; + tlschan_buf_datalen_mock_size = 1024; + MOCK(buf_datalen, tlschan_buf_datalen_mock); + len = ch->num_bytes_queued(ch); + tt_int_op(len, ==, tlschan_buf_datalen_mock_size); + /* + * We also cover num_cells_writeable here; since wide_circ_ids = 0 on + * the fake tlschans, cell_network_size returns 512, and so with + * tlschan_buf_datalen_mock_size == 1024, we should be able to write + * ceil((OR_CONN_HIGHWATER - 1024) / 512) = ceil(OR_CONN_HIGHWATER / 512) + * - 2 cells. + */ + n = ch->num_cells_writeable(ch); + tt_int_op(n, ==, CEIL_DIV(OR_CONN_HIGHWATER, 512) - 2); + UNMOCK(buf_datalen); + tlschan_buf_datalen_mock_target = NULL; + tlschan_buf_datalen_mock_size = 0; + if (fake_outbuf) { + buf_free(TO_CONN(tlschan->conn)->outbuf); + TO_CONN(tlschan->conn)->outbuf = NULL; + } + + done: + if (ch) { + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + /* + * Use fake close method that doesn't try to do too much to fake + * orconn + */ + ch->close = tlschan_fake_close_method; + channel_mark_for_close(ch); + free_fake_channel(ch); + UNMOCK(scheduler_release_channel); + } + + UNMOCK(connection_or_connect); + UNMOCK(is_local_addr); + + return; +} + +static void +test_channeltls_overhead_estimate(void *arg) +{ + tor_addr_t test_addr; + channel_t *ch = NULL; + const char test_digest[DIGEST_LEN] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 }; + float r; + channel_tls_t *tlschan = NULL; + + (void)arg; + + /* Set up a fake address to fake-connect to */ + test_addr.family = AF_INET; + test_addr.addr.in_addr.s_addr = htonl(0x01020304); + + /* For this test we always want the address to be treated as non-local */ + tlschan_local = 0; + /* Install is_local_addr() mock */ + MOCK(is_local_addr, tlschan_is_local_addr_mock); + + /* Install mock for connection_or_connect() */ + MOCK(connection_or_connect, tlschan_connection_or_connect_mock); + + /* Try connecting */ + ch = channel_tls_connect(&test_addr, 567, test_digest); + tt_assert(ch != NULL); + + /* First case: silly low ratios should get clamped to 1.0f */ + tlschan = BASE_CHAN_TO_TLS(ch); + tt_assert(tlschan != NULL); + tlschan->conn->bytes_xmitted = 128; + tlschan->conn->bytes_xmitted_by_tls = 64; + r = ch->get_overhead_estimate(ch); + tt_assert(fabsf(r - 1.0f) < 1E-12); + + tlschan->conn->bytes_xmitted_by_tls = 127; + r = ch->get_overhead_estimate(ch); + tt_assert(fabsf(r - 1.0f) < 1E-12); + + /* Now middle of the range */ + tlschan->conn->bytes_xmitted_by_tls = 192; + r = ch->get_overhead_estimate(ch); + tt_assert(fabsf(r - 1.5f) < 1E-12); + + /* Now above the 2.0f clamp */ + tlschan->conn->bytes_xmitted_by_tls = 257; + r = ch->get_overhead_estimate(ch); + tt_assert(fabsf(r - 2.0f) < 1E-12); + + tlschan->conn->bytes_xmitted_by_tls = 512; + r = ch->get_overhead_estimate(ch); + tt_assert(fabsf(r - 2.0f) < 1E-12); + + done: + if (ch) { + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + /* + * Use fake close method that doesn't try to do too much to fake + * orconn + */ + ch->close = tlschan_fake_close_method; + channel_mark_for_close(ch); + free_fake_channel(ch); + UNMOCK(scheduler_release_channel); + } + + UNMOCK(connection_or_connect); + UNMOCK(is_local_addr); + + return; +} + +static size_t +tlschan_buf_datalen_mock(const buf_t *buf) +{ + if (buf != NULL && buf == tlschan_buf_datalen_mock_target) { + return tlschan_buf_datalen_mock_size; + } else { + return buf_datalen__real(buf); + } +} + +static or_connection_t * +tlschan_connection_or_connect_mock(const tor_addr_t *addr, + uint16_t port, + const char *digest, + channel_tls_t *tlschan) +{ + or_connection_t *result = NULL; + + tt_assert(addr != NULL); + tt_assert(port != 0); + tt_assert(digest != NULL); + tt_assert(tlschan != NULL); + + /* Make a fake orconn */ + result = tor_malloc_zero(sizeof(*result)); + result->base_.magic = OR_CONNECTION_MAGIC; + result->base_.state = OR_CONN_STATE_OPEN; + result->base_.type = CONN_TYPE_OR; + result->base_.socket_family = addr->family; + result->base_.address = tor_strdup("<fake>"); + memcpy(&(result->base_.addr), addr, sizeof(tor_addr_t)); + result->base_.port = port; + memcpy(result->identity_digest, digest, DIGEST_LEN); + result->chan = tlschan; + memcpy(&(result->real_addr), addr, sizeof(tor_addr_t)); + result->tls = (tor_tls_t *)((void *)(&fake_tortls)); + + done: + return result; +} + +static void +tlschan_fake_close_method(channel_t *chan) +{ + channel_tls_t *tlschan = NULL; + + tt_assert(chan != NULL); + tt_int_op(chan->magic, ==, TLS_CHAN_MAGIC); + + tlschan = BASE_CHAN_TO_TLS(chan); + tt_assert(tlschan != NULL); + + /* Just free the fake orconn */ + tor_free(tlschan->conn->base_.address); + tor_free(tlschan->conn); + + channel_closed(chan); + + done: + return; +} + +static int +tlschan_is_local_addr_mock(const tor_addr_t *addr) +{ + tt_assert(addr != NULL); + + done: + return tlschan_local; +} + +struct testcase_t channeltls_tests[] = { + { "create", test_channeltls_create, TT_FORK, NULL, NULL }, + { "num_bytes_queued", test_channeltls_num_bytes_queued, + TT_FORK, NULL, NULL }, + { "overhead_estimate", test_channeltls_overhead_estimate, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_checkdir.c b/src/test/test_checkdir.c index 6c520656e0..882e3b3a61 100644 --- a/src/test/test_checkdir.c +++ b/src/test/test_checkdir.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Tor Project, Inc. */ +/* Copyright (c) 2014-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index 181aec20a9..0760accfc1 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Tor Project, Inc. */ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c index b8590d6d24..2a2a7ba145 100644 --- a/src/test/test_circuitmux.c +++ b/src/test/test_circuitmux.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Tor Project, Inc. */ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define TOR_CHANNEL_INTERNAL_ @@ -8,6 +8,7 @@ #include "channel.h" #include "circuitmux.h" #include "relay.h" +#include "scheduler.h" #include "test.h" /* XXXX duplicated function from test_circuitlist.c */ @@ -35,6 +36,12 @@ test_cmux_destroy_cell_queue(void *arg) circuit_t *circ = NULL; cell_queue_t *cq = NULL; packed_cell_t *pc = NULL; + tor_libevent_cfg cfg; + + memset(&cfg, 0, sizeof(cfg)); + + tor_libevent_initialize(&cfg); + scheduler_init(); #ifdef ENABLE_MEMPOOLS init_cell_pool(); diff --git a/src/test/test_cmdline_args.py b/src/test/test_cmdline_args.py index 55d1cdb805..c8e68e8240 100755 --- a/src/test/test_cmdline_args.py +++ b/src/test/test_cmdline_args.py @@ -57,14 +57,14 @@ def run_tor(args, failure=False): raise UnexpectedFailure() elif not result and failure: raise UnexpectedSuccess() - return b2s(output) + return b2s(output.replace('\r\n','\n')) def spaceify_fp(fp): for i in range(0, len(fp), 4): yield fp[i:i+4] def lines(s): - out = s.split("\n") + out = s.splitlines() if out and out[-1] == '': del out[-1] return out @@ -151,7 +151,7 @@ class CmdlineTests(unittest.TestCase): if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime: self.skipTest(TOR+" not up to date") out = run_tor(["--digests"]) - main_line = [ l for l in lines(out) if l.endswith("/main.c") ] + main_line = [ l for l in lines(out) if l.endswith("/main.c") or l.endswith(" main.c") ] digest, name = main_line[0].split() f = open(main_c, 'rb') actual = hashlib.sha1(f.read()).hexdigest() diff --git a/src/test/test_config.c b/src/test/test_config.c index dcb4e92c56..0b411e3354 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -1,11 +1,12 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" #define CONFIG_PRIVATE +#define PT_PRIVATE #include "or.h" #include "addressmap.h" #include "config.h" @@ -578,6 +579,8 @@ pt_kickstart_proxy_mock(const smartlist_t *transport_list, /* XXXX check that args are as expected. */ ++pt_kickstart_proxy_mock_call_count; + + free_execve_args(proxy_argv); } static int @@ -853,10 +856,599 @@ test_config_fix_my_family(void *arg) or_options_free(defaults); } +static int n_hostname_01010101 = 0; + +/** This mock function is meant to replace tor_lookup_hostname(). + * It answers with 1.1.1.1 as IP adddress that resulted from lookup. + * This function increments <b>n_hostname_01010101</b> counter by one + * every time it is called. + */ +static int +tor_lookup_hostname_01010101(const char *name, uint32_t *addr) +{ + n_hostname_01010101++; + + if (name && addr) { + *addr = ntohl(0x01010101); + } + + return 0; +} + +static int n_hostname_localhost = 0; + +/** This mock function is meant to replace tor_lookup_hostname(). + * It answers with 127.0.0.1 as IP adddress that resulted from lookup. + * This function increments <b>n_hostname_localhost</b> counter by one + * every time it is called. + */ +static int +tor_lookup_hostname_localhost(const char *name, uint32_t *addr) +{ + n_hostname_localhost++; + + if (name && addr) { + *addr = 0x7f000001; + } + + return 0; +} + +static int n_hostname_failure = 0; + +/** This mock function is meant to replace tor_lookup_hostname(). + * It pretends to fail by returning -1 to caller. Also, this function + * increments <b>n_hostname_failure</b> every time it is called. + */ +static int +tor_lookup_hostname_failure(const char *name, uint32_t *addr) +{ + (void)name; + (void)addr; + + n_hostname_failure++; + + return -1; +} + +static int n_gethostname_replacement = 0; + +/** This mock function is meant to replace tor_gethostname(). It + * responds with string "onionrouter!" as hostname. This function + * increments <b>n_gethostname_replacement</b> by one every time + * it is called. + */ +static int +tor_gethostname_replacement(char *name, size_t namelen) +{ + n_gethostname_replacement++; + + if (name && namelen) { + strlcpy(name,"onionrouter!",namelen); + } + + return 0; +} + +static int n_gethostname_localhost = 0; + +/** This mock function is meant to replace tor_gethostname(). It + * responds with string "127.0.0.1" as hostname. This function + * increments <b>n_gethostname_localhost</b> by one every time + * it is called. + */ +static int +tor_gethostname_localhost(char *name, size_t namelen) +{ + n_gethostname_localhost++; + + if (name && namelen) { + strlcpy(name,"127.0.0.1",namelen); + } + + return 0; +} + +static int n_gethostname_failure = 0; + +/** This mock function is meant to replace tor_gethostname. + * It pretends to fail by returning -1. This function increments + * <b>n_gethostname_failure</b> by one every time it is called. + */ +static int +tor_gethostname_failure(char *name, size_t namelen) +{ + (void)name; + (void)namelen; + n_gethostname_failure++; + + return -1; +} + +static int n_get_interface_address = 0; + +/** This mock function is meant to replace get_interface_address(). + * It answers with address 8.8.8.8. This function increments + * <b>n_get_interface_address</b> by one every time it is called. + */ +static int +get_interface_address_08080808(int severity, uint32_t *addr) +{ + (void)severity; + + n_get_interface_address++; + + if (addr) { + *addr = ntohl(0x08080808); + } + + return 0; +} + +static int n_get_interface_address6 = 0; +static sa_family_t last_address6_family; + +/** This mock function is meant to replace get_interface_address6(). + * It answers with IP address 9.9.9.9 iff both of the following are true: + * - <b>family</b> is AF_INET + * - <b>addr</b> pointer is not NULL. + * This function increments <b>n_get_interface_address6</b> by one every + * time it is called. + */ +static int +get_interface_address6_replacement(int severity, sa_family_t family, + tor_addr_t *addr) +{ + (void)severity; + + last_address6_family = family; + n_get_interface_address6++; + + if ((family != AF_INET) || !addr) { + return -1; + } + + tor_addr_from_ipv4h(addr,0x09090909); + + return 0; +} + +static int n_get_interface_address_failure = 0; + +/** + * This mock function is meant to replace get_interface_address(). + * It pretends to fail getting interface address by returning -1. + * <b>n_get_interface_address_failure</b> is incremented by one + * every time this function is called. + */ +static int +get_interface_address_failure(int severity, uint32_t *addr) +{ + (void)severity; + (void)addr; + + n_get_interface_address_failure++; + + return -1; +} + +static int n_get_interface_address6_failure = 0; + +/** + * This mock function is meant to replace get_interface_addres6(). + * It will pretent to fail by return -1. + * <b>n_get_interface_address6_failure</b> is incremented by one + * every time this function is called and <b>last_address6_family</b> + * is assigned the value of <b>family</b> argument. + */ +static int +get_interface_address6_failure(int severity, sa_family_t family, + tor_addr_t *addr) +{ + (void)severity; + (void)addr; + n_get_interface_address6_failure++; + last_address6_family = family; + + return -1; +} + +static void +test_config_resolve_my_address(void *arg) +{ + or_options_t *options; + uint32_t resolved_addr; + const char *method_used; + char *hostname_out = NULL; + int retval; + int prev_n_hostname_01010101; + int prev_n_hostname_localhost; + int prev_n_hostname_failure; + int prev_n_gethostname_replacement; + int prev_n_gethostname_failure; + int prev_n_gethostname_localhost; + int prev_n_get_interface_address; + int prev_n_get_interface_address_failure; + int prev_n_get_interface_address6; + int prev_n_get_interface_address6_failure; + + (void)arg; + + options = options_new(); + + options_init(options); + + /* + * CASE 1: + * If options->Address is a valid IPv4 address string, we want + * the corresponding address to be parsed and returned. + */ + + options->Address = tor_strdup("128.52.128.105"); + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(retval == 0); + tt_want_str_op(method_used,==,"CONFIGURED"); + tt_want(hostname_out == NULL); + tt_assert(htonl(resolved_addr) == 0x69803480); + + tor_free(options->Address); + +/* + * CASE 2: + * If options->Address is a valid DNS address, we want resolve_my_address() + * function to ask tor_lookup_hostname() for help with resolving it + * and return the address that was resolved (in host order). + */ + + MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + + tor_free(options->Address); + options->Address = tor_strdup("www.torproject.org"); + + prev_n_hostname_01010101 = n_hostname_01010101; + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(retval == 0); + tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); + tt_want_str_op(method_used,==,"RESOLVED"); + tt_want_str_op(hostname_out,==,"www.torproject.org"); + tt_assert(htonl(resolved_addr) == 0x01010101); + + UNMOCK(tor_lookup_hostname); + + tor_free(options->Address); + tor_free(hostname_out); + +/* + * CASE 3: + * Given that options->Address is NULL, we want resolve_my_address() + * to try and use tor_gethostname() to get hostname AND use + * tor_lookup_hostname() to get IP address. + */ + + resolved_addr = 0; + tor_free(options->Address); + options->Address = NULL; + + MOCK(tor_gethostname,tor_gethostname_replacement); + MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + + prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_hostname_01010101 = n_hostname_01010101; + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(retval == 0); + tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); + tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); + tt_want_str_op(method_used,==,"GETHOSTNAME"); + tt_want_str_op(hostname_out,==,"onionrouter!"); + tt_assert(htonl(resolved_addr) == 0x01010101); + + UNMOCK(tor_gethostname); + UNMOCK(tor_lookup_hostname); + + tor_free(hostname_out); + +/* + * CASE 4: + * Given that options->Address is a local host address, we want + * resolve_my_address() function to fail. + */ + + resolved_addr = 0; + tor_free(options->Address); + options->Address = tor_strdup("127.0.0.1"); + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(resolved_addr == 0); + tt_assert(retval == -1); + + tor_free(options->Address); + tor_free(hostname_out); + +/* + * CASE 5: + * We want resolve_my_address() to fail if DNS address in options->Address + * cannot be resolved. + */ + + MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + + prev_n_hostname_failure = n_hostname_failure; + + tor_free(options->Address); + options->Address = tor_strdup("www.tor-project.org"); + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(n_hostname_failure == prev_n_hostname_failure + 1); + tt_assert(retval == -1); + + UNMOCK(tor_lookup_hostname); + + tor_free(options->Address); + tor_free(hostname_out); + +/* + * CASE 6: + * If options->Address is NULL AND gettting local hostname fails, we want + * resolve_my_address() to fail as well. + */ + + MOCK(tor_gethostname,tor_gethostname_failure); + + prev_n_gethostname_failure = n_gethostname_failure; + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(n_gethostname_failure == prev_n_gethostname_failure + 1); + tt_assert(retval == -1); + + UNMOCK(tor_gethostname); + tor_free(hostname_out); + + +/* + * CASE 7: + * We want resolve_my_address() to try and get network interface address via + * get_interface_address() if hostname returned by tor_gethostname() cannot be + * resolved into IP address. + */ + + MOCK(tor_gethostname,tor_gethostname_replacement); + MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + MOCK(get_interface_address,get_interface_address_08080808); + + prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_get_interface_address = n_get_interface_address; + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(retval == 0); + tt_want_int_op(n_gethostname_replacement, ==, + prev_n_gethostname_replacement + 1); + tt_want_int_op(n_get_interface_address, ==, + prev_n_get_interface_address + 1); + tt_want_str_op(method_used,==,"INTERFACE"); + tt_want(hostname_out == NULL); + tt_assert(resolved_addr == ntohl(0x08080808)); + + UNMOCK(get_interface_address); + tor_free(hostname_out); + +/* + * CASE 8: + * Suppose options->Address is NULL AND hostname returned by tor_gethostname() + * is unresolvable. We want resolve_my_address to fail if + * get_interface_address() fails. + */ + + MOCK(get_interface_address,get_interface_address_failure); + + prev_n_get_interface_address_failure = n_get_interface_address_failure; + prev_n_gethostname_replacement = n_gethostname_replacement; + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(n_get_interface_address_failure == + prev_n_get_interface_address_failure + 1); + tt_want(n_gethostname_replacement == + prev_n_gethostname_replacement + 1); + tt_assert(retval == -1); + + UNMOCK(get_interface_address); + tor_free(hostname_out); + +/* + * CASE 9: + * Given that options->Address is NULL AND tor_lookup_hostname() + * fails AND hostname returned by gethostname() resolves + * to local IP address, we want resolve_my_address() function to + * call get_interface_address6(.,AF_INET,.) and return IP address + * the latter function has found. + */ + + MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + MOCK(tor_gethostname,tor_gethostname_replacement); + MOCK(get_interface_address6,get_interface_address6_replacement); + + prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_hostname_failure = n_hostname_failure; + prev_n_get_interface_address6 = n_get_interface_address6; + + retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, + &method_used,&hostname_out); + + tt_want(last_address6_family == AF_INET); + tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1); + tt_want(n_hostname_failure == prev_n_hostname_failure + 1); + tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); + tt_want(retval == 0); + tt_want_str_op(method_used,==,"INTERFACE"); + tt_assert(htonl(resolved_addr) == 0x09090909); + + UNMOCK(tor_lookup_hostname); + UNMOCK(tor_gethostname); + UNMOCK(get_interface_address6); + + tor_free(hostname_out); + + /* + * CASE 10: We want resolve_my_address() to fail if all of the following + * are true: + * 1. options->Address is not NULL + * 2. ... but it cannot be converted to struct in_addr by + * tor_inet_aton() + * 3. ... and tor_lookup_hostname() fails to resolve the + * options->Address + */ + + MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + + prev_n_hostname_failure = n_hostname_failure; + + tor_free(options->Address); + options->Address = tor_strdup("some_hostname"); + + retval = resolve_my_address(LOG_NOTICE, options, &resolved_addr, + &method_used,&hostname_out); + + tt_want(n_hostname_failure == prev_n_hostname_failure + 1); + tt_assert(retval == -1); + + UNMOCK(tor_gethostname); + UNMOCK(tor_lookup_hostname); + + tor_free(hostname_out); + + /* + * CASE 11: + * Suppose the following sequence of events: + * 1. options->Address is NULL + * 2. tor_gethostname() succeeds to get hostname of machine Tor + * if running on. + * 3. Hostname from previous step cannot be converted to + * address by using tor_inet_aton() function. + * 4. However, tor_lookup_hostname() succeds in resolving the + * hostname from step 2. + * 5. Unfortunately, tor_addr_is_internal() deems this address + * to be internal. + * 6. get_interface_address6(.,AF_INET,.) returns non-internal + * IPv4 + * + * We want resolve_my_addr() to succeed with method "INTERFACE" + * and address from step 6. + */ + + tor_free(options->Address); + options->Address = NULL; + + MOCK(tor_gethostname,tor_gethostname_replacement); + MOCK(tor_lookup_hostname,tor_lookup_hostname_localhost); + MOCK(get_interface_address6,get_interface_address6_replacement); + + prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_hostname_localhost = n_hostname_localhost; + prev_n_get_interface_address6 = n_get_interface_address6; + + retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, + &method_used,&hostname_out); + + tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); + tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); + tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1); + + tt_str_op(method_used,==,"INTERFACE"); + tt_assert(!hostname_out); + tt_assert(retval == 0); + + /* + * CASE 11b: + * 1-5 as above. + * 6. get_interface_address6() fails. + * + * In this subcase, we want resolve_my_address() to fail. + */ + + UNMOCK(get_interface_address6); + MOCK(get_interface_address6,get_interface_address6_failure); + + prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_hostname_localhost = n_hostname_localhost; + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + + retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, + &method_used,&hostname_out); + + tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); + tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); + tt_want(n_get_interface_address6_failure == + prev_n_get_interface_address6_failure + 1); + + tt_assert(retval == -1); + + UNMOCK(tor_gethostname); + UNMOCK(tor_lookup_hostname); + UNMOCK(get_interface_address6); + + /* CASE 12: + * Suppose the following happens: + * 1. options->Address is NULL AND options->DirAuthorities is 1. + * 2. tor_gethostname() succeeds in getting hostname of a machine ... + * 3. ... which is successfully parsed by tor_inet_aton() ... + * 4. into IPv4 address that tor_addr_is_inernal() considers to be + * internal. + * + * In this case, we want resolve_my_address() to fail. + */ + + tor_free(options->Address); + options->Address = NULL; + options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t)); + + MOCK(tor_gethostname,tor_gethostname_localhost); + + prev_n_gethostname_localhost = n_gethostname_localhost; + + retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, + &method_used,&hostname_out); + + tt_want(n_gethostname_localhost == prev_n_gethostname_localhost + 1); + tt_assert(retval == -1); + + UNMOCK(tor_gethostname); + + done: + tor_free(options->Address); + tor_free(options->DirAuthorities); + or_options_free(options); + tor_free(hostname_out); + + UNMOCK(tor_gethostname); + UNMOCK(tor_lookup_hostname); + UNMOCK(get_interface_address); + UNMOCK(get_interface_address6); + UNMOCK(tor_gethostname); +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } struct testcase_t config_tests[] = { + CONFIG_TEST(resolve_my_address, TT_FORK), CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), diff --git a/src/test/test_containers.c b/src/test/test_containers.c index 1eb8c15fd5..79085a748e 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index dd9d590ec7..e36314da45 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Tor Project, Inc. */ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONNECTION_PRIVATE diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 5352b9fdb4..4a5a12c50a 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_data.c b/src/test/test_data.c index 0e6f79f33c..6afba65757 100644 --- a/src/test/test_data.c +++ b/src/test/test_data.c @@ -1,6 +1,6 @@ /* Copyright 2001-2004 Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Our unit test expect that the AUTHORITY_CERT_* public keys will sort diff --git a/src/test/test_dir.c b/src/test/test_dir.c index e5328fed56..c6594f8359 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -388,6 +388,12 @@ test_dir_routerparse_bad(void *arg) #include "example_extrainfo.inc" static void +routerinfo_free_wrapper_(void *arg) +{ + routerinfo_free(arg); +} + +static void test_dir_extrainfo_parsing(void *arg) { (void) arg; @@ -455,9 +461,9 @@ test_dir_extrainfo_parsing(void *arg) #undef CHECK_FAIL done: + extrainfo_free(ei); routerinfo_free(ri); - /* XXXX elements should get freed too */ - digestmap_free((digestmap_t*)map, NULL); + digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_); } static void @@ -552,9 +558,8 @@ test_dir_parse_router_list(void *arg) SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); smartlist_free(chunks); routerinfo_free(ri); - /* XXXX this leaks: */ if (map) { - digestmap_free((digestmap_t*)map, NULL); + digestmap_free((digestmap_t*)map, routerinfo_free_wrapper_); router_get_routerlist()->identity_map = (struct digest_ri_map_t*)digestmap_new(); } @@ -605,6 +610,7 @@ test_dir_load_routers(void *arg) smartlist_t *wanted = smartlist_new(); char buf[DIGEST_LEN]; char *mem_op_hex_tmp = NULL; + char *list = NULL; #define ADD(str) \ do { \ @@ -630,7 +636,7 @@ test_dir_load_routers(void *arg) /* Not ADDing BAD_PORTS */ ADD(EX_RI_BAD_TOKENS); - char *list = smartlist_join_strings(chunks, "", 0, NULL); + list = smartlist_join_strings(chunks, "", 0, NULL); tt_int_op(1, OP_EQ, router_load_routers_from_string(list, NULL, SAVED_IN_JOURNAL, wanted, 1, NULL)); @@ -670,6 +676,7 @@ test_dir_load_routers(void *arg) smartlist_free(chunks); SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp)); smartlist_free(wanted); + tor_free(list); } static int mock_get_by_ei_dd_calls = 0; @@ -722,6 +729,7 @@ test_dir_load_extrainfo(void *arg) smartlist_t *wanted = smartlist_new(); char buf[DIGEST_LEN]; char *mem_op_hex_tmp = NULL; + char *list = NULL; #define ADD(str) \ do { \ @@ -746,7 +754,7 @@ test_dir_load_extrainfo(void *arg) ADD(EX_EI_BAD_TOKENS); ADD(EX_EI_BAD_SIG2); - char *list = smartlist_join_strings(chunks, "", 0, NULL); + list = smartlist_join_strings(chunks, "", 0, NULL); router_load_extrainfo_from_string(list, NULL, SAVED_IN_JOURNAL, wanted, 1); /* The "maximal" router was added. */ @@ -788,6 +796,7 @@ test_dir_load_extrainfo(void *arg) smartlist_free(chunks); SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp)); smartlist_free(wanted); + tor_free(list); } static void @@ -836,6 +845,42 @@ test_dir_versions(void *arg) tt_int_op(VER_RELEASE,OP_EQ, ver1.status); tt_str_op("",OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("10.1", &ver1)); + tt_int_op(10, OP_EQ, ver1.major); + tt_int_op(1, OP_EQ, ver1.minor); + tt_int_op(0, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("5.99.999", &ver1)); + tt_int_op(5, OP_EQ, ver1.major); + tt_int_op(99, OP_EQ, ver1.minor); + tt_int_op(999, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("10.1-alpha", &ver1)); + tt_int_op(10, OP_EQ, ver1.major); + tt_int_op(1, OP_EQ, ver1.minor); + tt_int_op(0, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("alpha", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("2.1.700-alpha", &ver1)); + tt_int_op(2, OP_EQ, ver1.major); + tt_int_op(1, OP_EQ, ver1.minor); + tt_int_op(700, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("alpha", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("1.6.8-alpha-dev", &ver1)); + tt_int_op(1, OP_EQ, ver1.major); + tt_int_op(6, OP_EQ, ver1.minor); + tt_int_op(8, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("alpha-dev", OP_EQ, ver1.status_tag); + #define tt_versionstatus_op(vs1, op, vs2) \ tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \ (val1_ op val2_),"%d",TT_EXIT_TEST_FUNCTION) diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 5bf2985d2f..19071a1550 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Tor Project, Inc. */ +/* Copyright (c) 2014-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index d99961dd4a..2e5a32eef3 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Tor Project, Inc. */ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONNECTION_PRIVATE diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 2db88a4c0c..0246eaf648 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2014, The Tor Project, Inc. */ +/* Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -99,18 +99,20 @@ test_hs_desc_event(void *arg) /* test failed event */ rend_query.auth_type = 2; - control_event_hs_descriptor_failed(&rend_query, HSDIR_NONE_EXIST_ID); + control_event_hs_descriptor_failed(&rend_query, HSDIR_NONE_EXIST_ID, + "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ - STR_HSDIR_NONE_EXIST_LONGNAME"\r\n"; + STR_HSDIR_NONE_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n"; tt_assert(received_msg); tt_str_op(received_msg,OP_EQ, expected_msg); tor_free(received_msg); /* test invalid auth type */ rend_query.auth_type = 999; - control_event_hs_descriptor_failed(&rend_query, HSDIR_EXIST_ID); + control_event_hs_descriptor_failed(&rend_query, HSDIR_EXIST_ID, + "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ - STR_HSDIR_EXIST_LONGNAME"\r\n"; + STR_HSDIR_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n"; tt_assert(received_msg); tt_str_op(received_msg,OP_EQ, expected_msg); tor_free(received_msg); diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index fe8ffbfa4b..0cab8ef4cc 100644 --- a/src/test/test_introduce.c +++ b/src/test/test_introduce.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Tor Project, Inc. */ +/* Copyright (c) 2012-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_logging.c b/src/test/test_logging.c index 17f1ed566c..6205b3bdc5 100644 --- a/src/test/test_logging.c +++ b/src/test/test_logging.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Tor Project, Inc. */ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 4a7c29b747..fb3df77edc 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2014, The Tor Project, Inc. */ +/* Copyright (c) 2010-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -703,8 +703,7 @@ test_md_reject_cache(void *arg) done: UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(router_get_mutable_consensus_status_by_descriptor_digest); - if (options) - tor_free(options->DataDirectory); + tor_free(options->DataDirectory); microdesc_free_all(); smartlist_free(added); SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp)); diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index 2fba3da7e0..9bd8b4a7ea 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2014, The Tor Project, Inc. */ +/* Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c index 2899ad6710..955b508ef0 100644 --- a/src/test/test_ntor_cl.c +++ b/src/test/test_ntor_cl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Tor Project, Inc. */ +/* Copyright (c) 2012-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_oom.c b/src/test/test_oom.c index 1f21f65c60..28b4c0435a 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Tor Project, Inc. */ +/* Copyright (c) 2014-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for OOM handling logic */ diff --git a/src/test/test_options.c b/src/test/test_options.c index 44349b3800..a8ebadb14b 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CONFIG_PRIVATE diff --git a/src/test/test_policy.c b/src/test/test_policy.c index e77e16c99e..33f90c7da5 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Tor Project, Inc. */ +/* Copyright (c) 2013-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_pt.c b/src/test/test_pt.c index dba880ee19..996ef8666b 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_relay.c b/src/test/test_relay.c new file mode 100644 index 0000000000..2144ef335e --- /dev/null +++ b/src/test/test_relay.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2014-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#define CIRCUITBUILD_PRIVATE +#include "circuitbuild.h" +#define RELAY_PRIVATE +#include "relay.h" +/* For init/free stuff */ +#include "scheduler.h" + +/* Test suite stuff */ +#include "test.h" +#include "fakechans.h" + +static or_circuit_t * new_fake_orcirc(channel_t *nchan, channel_t *pchan); + +static void test_relay_append_cell_to_circuit_queue(void *arg); + +static or_circuit_t * +new_fake_orcirc(channel_t *nchan, channel_t *pchan) +{ + or_circuit_t *orcirc = NULL; + circuit_t *circ = NULL; + + orcirc = tor_malloc_zero(sizeof(*orcirc)); + circ = &(orcirc->base_); + circ->magic = OR_CIRCUIT_MAGIC; + + circ->n_chan = nchan; + circ->n_circ_id = get_unique_circ_id_by_chan(nchan); + circ->n_mux = NULL; /* ?? */ + cell_queue_init(&(circ->n_chan_cells)); + circ->n_hop = NULL; + circ->streams_blocked_on_n_chan = 0; + circ->streams_blocked_on_p_chan = 0; + circ->n_delete_pending = 0; + circ->p_delete_pending = 0; + circ->received_destroy = 0; + circ->state = CIRCUIT_STATE_OPEN; + circ->purpose = CIRCUIT_PURPOSE_OR; + circ->package_window = CIRCWINDOW_START_MAX; + circ->deliver_window = CIRCWINDOW_START_MAX; + circ->n_chan_create_cell = NULL; + + orcirc->p_chan = pchan; + orcirc->p_circ_id = get_unique_circ_id_by_chan(pchan); + cell_queue_init(&(orcirc->p_chan_cells)); + + return orcirc; +} + +static void +test_relay_append_cell_to_circuit_queue(void *arg) +{ + channel_t *nchan = NULL, *pchan = NULL; + or_circuit_t *orcirc = NULL; + cell_t *cell = NULL; + int old_count, new_count; + + (void)arg; + + /* We'll need the cell pool for append_cell_to_circuit_queue() to work */ +#ifdef ENABLE_MEMPOOLS + init_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + /* Make fake channels to be nchan and pchan for the circuit */ + nchan = new_fake_channel(); + tt_assert(nchan); + + pchan = new_fake_channel(); + tt_assert(pchan); + + /* We'll need chans with working cmuxes */ + nchan->cmux = circuitmux_alloc(); + pchan->cmux = circuitmux_alloc(); + + /* Make a fake orcirc */ + orcirc = new_fake_orcirc(nchan, pchan); + tt_assert(orcirc); + + /* Make a cell */ + cell = tor_malloc_zero(sizeof(cell_t)); + make_fake_cell(cell); + + MOCK(scheduler_channel_has_waiting_cells, + scheduler_channel_has_waiting_cells_mock); + + /* Append it */ + old_count = get_mock_scheduler_has_waiting_cells_count(); + append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), nchan, cell, + CELL_DIRECTION_OUT, 0); + new_count = get_mock_scheduler_has_waiting_cells_count(); + tt_int_op(new_count, ==, old_count + 1); + + /* Now try the reverse direction */ + old_count = get_mock_scheduler_has_waiting_cells_count(); + append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), pchan, cell, + CELL_DIRECTION_IN, 0); + new_count = get_mock_scheduler_has_waiting_cells_count(); + tt_int_op(new_count, ==, old_count + 1); + + UNMOCK(scheduler_channel_has_waiting_cells); + + /* Get rid of the fake channels */ + MOCK(scheduler_release_channel, scheduler_release_channel_mock); + channel_mark_for_close(nchan); + channel_mark_for_close(pchan); + UNMOCK(scheduler_release_channel); + + /* Shut down channels */ + channel_free_all(); + + done: + tor_free(cell); + cell_queue_clear(&orcirc->base_.n_chan_cells); + cell_queue_clear(&orcirc->p_chan_cells); + tor_free(orcirc); + free_fake_channel(nchan); + free_fake_channel(pchan); +#ifdef ENABLE_MEMPOOLS + free_cell_pool(); +#endif /* ENABLE_MEMPOOLS */ + + return; +} + +struct testcase_t relay_tests[] = { + { "append_cell_to_circuit_queue", test_relay_append_cell_to_circuit_queue, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c index 834dfeface..fafb5bbbea 100644 --- a/src/test/test_relaycell.c +++ b/src/test/test_relaycell.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Tor Project, Inc. */ +/* Copyright (c) 2014-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* Unit tests for handling different kinds of relay cell */ diff --git a/src/test/test_replay.c b/src/test/test_replay.c index b1f637a43b..a02c160365 100644 --- a/src/test/test_replay.c +++ b/src/test/test_replay.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Tor Project, Inc. */ +/* Copyright (c) 2012-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define REPLAYCACHE_PRIVATE diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index d8ad59a58b..60b6bb5a72 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c new file mode 100644 index 0000000000..73a422088f --- /dev/null +++ b/src/test/test_scheduler.c @@ -0,0 +1,763 @@ +/* Copyright (c) 2014-2015, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include <math.h> + +#include "orconfig.h" + +/* Libevent stuff */ +#ifdef HAVE_EVENT2_EVENT_H +#include <event2/event.h> +#else +#include <event.h> +#endif + +#define TOR_CHANNEL_INTERNAL_ +#define CHANNEL_PRIVATE_ +#include "or.h" +#include "compat_libevent.h" +#include "channel.h" +#define SCHEDULER_PRIVATE_ +#include "scheduler.h" + +/* Test suite stuff */ +#include "test.h" +#include "fakechans.h" + +/* Statics in scheduler.c exposed to the test suite */ +extern smartlist_t *channels_pending; +extern struct event *run_sched_ev; +extern uint64_t queue_heuristic; +extern time_t queue_heuristic_timestamp; + +/* Event base for scheduelr tests */ +static struct event_base *mock_event_base = NULL; + +/* Statics controlling mocks */ +static circuitmux_t *mock_ccm_tgt_1 = NULL; +static circuitmux_t *mock_ccm_tgt_2 = NULL; + +static circuitmux_t *mock_cgp_tgt_1 = NULL; +static const circuitmux_policy_t *mock_cgp_val_1 = NULL; +static circuitmux_t *mock_cgp_tgt_2 = NULL; +static const circuitmux_policy_t *mock_cgp_val_2 = NULL; +static int scheduler_compare_channels_mock_ctr = 0; +static int scheduler_run_mock_ctr = 0; + +static void channel_flush_some_cells_mock_free_all(void); +static void channel_flush_some_cells_mock_set(channel_t *chan, + ssize_t num_cells); + +/* Setup for mock event stuff */ +static void mock_event_free_all(void); +static void mock_event_init(void); + +/* Mocks used by scheduler tests */ +static ssize_t channel_flush_some_cells_mock(channel_t *chan, + ssize_t num_cells); +static int circuitmux_compare_muxes_mock(circuitmux_t *cmux_1, + circuitmux_t *cmux_2); +static const circuitmux_policy_t * circuitmux_get_policy_mock( + circuitmux_t *cmux); +static int scheduler_compare_channels_mock(const void *c1_v, + const void *c2_v); +static void scheduler_run_noop_mock(void); +static struct event_base * tor_libevent_get_base_mock(void); + +/* Scheduler test cases */ +static void test_scheduler_channel_states(void *arg); +static void test_scheduler_compare_channels(void *arg); +static void test_scheduler_initfree(void *arg); +static void test_scheduler_loop(void *arg); +static void test_scheduler_queue_heuristic(void *arg); + +/* Mock event init/free */ + +/* Shamelessly stolen from compat_libevent.c */ +#define V(major, minor, patch) \ + (((major) << 24) | ((minor) << 16) | ((patch) << 8)) + +static void +mock_event_free_all(void) +{ + tt_assert(mock_event_base != NULL); + + if (mock_event_base) { + event_base_free(mock_event_base); + mock_event_base = NULL; + } + + tt_ptr_op(mock_event_base, ==, NULL); + + done: + return; +} + +static void +mock_event_init(void) +{ +#ifdef HAVE_EVENT2_EVENT_H + struct event_config *cfg = NULL; +#endif + + tt_ptr_op(mock_event_base, ==, NULL); + + /* + * Really cut down from tor_libevent_initialize of + * src/common/compat_libevent.c to kill config dependencies + */ + + if (!mock_event_base) { +#ifdef HAVE_EVENT2_EVENT_H + cfg = event_config_new(); +#if LIBEVENT_VERSION_NUMBER >= V(2,0,9) + /* We can enable changelist support with epoll, since we don't give + * Libevent any dup'd fds. This lets us avoid some syscalls. */ + event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST); +#endif + mock_event_base = event_base_new_with_config(cfg); + event_config_free(cfg); +#else + mock_event_base = event_init(); +#endif + } + + tt_assert(mock_event_base != NULL); + + done: + return; +} + +/* Mocks */ + +typedef struct { + const channel_t *chan; + ssize_t cells; +} flush_mock_channel_t; + +static smartlist_t *chans_for_flush_mock = NULL; + +static void +channel_flush_some_cells_mock_free_all(void) +{ + if (chans_for_flush_mock) { + SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock, + flush_mock_channel_t *, + flush_mock_ch) { + SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch); + tor_free(flush_mock_ch); + } SMARTLIST_FOREACH_END(flush_mock_ch); + + smartlist_free(chans_for_flush_mock); + chans_for_flush_mock = NULL; + } +} + +static void +channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells) +{ + flush_mock_channel_t *flush_mock_ch = NULL; + + if (!chan) return; + if (num_cells <= 0) return; + + if (!chans_for_flush_mock) { + chans_for_flush_mock = smartlist_new(); + } + + SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock, + flush_mock_channel_t *, + flush_mock_ch) { + if (flush_mock_ch != NULL && flush_mock_ch->chan != NULL) { + if (flush_mock_ch->chan == chan) { + /* Found it */ + flush_mock_ch->cells = num_cells; + break; + } + } else { + /* That shouldn't be there... */ + SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch); + tor_free(flush_mock_ch); + } + } SMARTLIST_FOREACH_END(flush_mock_ch); + + if (!flush_mock_ch) { + /* The loop didn't find it */ + flush_mock_ch = tor_malloc_zero(sizeof(*flush_mock_ch)); + flush_mock_ch->chan = chan; + flush_mock_ch->cells = num_cells; + smartlist_add(chans_for_flush_mock, flush_mock_ch); + } +} + +static ssize_t +channel_flush_some_cells_mock(channel_t *chan, ssize_t num_cells) +{ + ssize_t flushed = 0, max; + char unlimited = 0; + flush_mock_channel_t *found = NULL; + + tt_assert(chan != NULL); + if (chan) { + if (num_cells < 0) { + num_cells = 0; + unlimited = 1; + } + + /* Check if we have it */ + if (chans_for_flush_mock != NULL) { + SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock, + flush_mock_channel_t *, + flush_mock_ch) { + if (flush_mock_ch != NULL && flush_mock_ch->chan != NULL) { + if (flush_mock_ch->chan == chan) { + /* Found it */ + found = flush_mock_ch; + break; + } + } else { + /* That shouldn't be there... */ + SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch); + tor_free(flush_mock_ch); + } + } SMARTLIST_FOREACH_END(flush_mock_ch); + + if (found) { + /* We found one */ + if (found->cells < 0) found->cells = 0; + + if (unlimited) max = found->cells; + else max = MIN(found->cells, num_cells); + + flushed += max; + found->cells -= max; + + if (found->cells <= 0) { + smartlist_remove(chans_for_flush_mock, found); + tor_free(found); + } + } + } + } + + done: + return flushed; +} + +static int +circuitmux_compare_muxes_mock(circuitmux_t *cmux_1, + circuitmux_t *cmux_2) +{ + int result = 0; + + tt_assert(cmux_1 != NULL); + tt_assert(cmux_2 != NULL); + + if (cmux_1 != cmux_2) { + if (cmux_1 == mock_ccm_tgt_1 && cmux_2 == mock_ccm_tgt_2) result = -1; + else if (cmux_1 == mock_ccm_tgt_2 && cmux_2 == mock_ccm_tgt_1) { + result = 1; + } else { + if (cmux_1 == mock_ccm_tgt_1 || cmux_1 == mock_ccm_tgt_2) result = -1; + else if (cmux_2 == mock_ccm_tgt_1 || cmux_2 == mock_ccm_tgt_2) { + result = 1; + } else { + result = circuitmux_compare_muxes__real(cmux_1, cmux_2); + } + } + } + /* else result = 0 always */ + + done: + return result; +} + +static const circuitmux_policy_t * +circuitmux_get_policy_mock(circuitmux_t *cmux) +{ + const circuitmux_policy_t *result = NULL; + + tt_assert(cmux != NULL); + if (cmux) { + if (cmux == mock_cgp_tgt_1) result = mock_cgp_val_1; + else if (cmux == mock_cgp_tgt_2) result = mock_cgp_val_2; + else result = circuitmux_get_policy__real(cmux); + } + + done: + return result; +} + +static int +scheduler_compare_channels_mock(const void *c1_v, + const void *c2_v) +{ + uintptr_t p1, p2; + + p1 = (uintptr_t)(c1_v); + p2 = (uintptr_t)(c2_v); + + ++scheduler_compare_channels_mock_ctr; + + if (p1 == p2) return 0; + else if (p1 < p2) return 1; + else return -1; +} + +static void +scheduler_run_noop_mock(void) +{ + ++scheduler_run_mock_ctr; +} + +static struct event_base * +tor_libevent_get_base_mock(void) +{ + return mock_event_base; +} + +/* Test cases */ + +static void +test_scheduler_channel_states(void *arg) +{ + channel_t *ch1 = NULL, *ch2 = NULL; + int old_count; + + (void)arg; + + /* Set up libevent and scheduler */ + + mock_event_init(); + MOCK(tor_libevent_get_base, tor_libevent_get_base_mock); + scheduler_init(); + /* + * Install the compare channels mock so we can test + * scheduler_touch_channel(). + */ + MOCK(scheduler_compare_channels, scheduler_compare_channels_mock); + /* + * Disable scheduler_run so we can just check the state transitions + * without having to make everything it might call work too. + */ + MOCK(scheduler_run, scheduler_run_noop_mock); + + tt_int_op(smartlist_len(channels_pending), ==, 0); + + /* Set up a fake channel */ + ch1 = new_fake_channel(); + tt_assert(ch1); + + /* Start it off in OPENING */ + ch1->state = CHANNEL_STATE_OPENING; + /* We'll need a cmux */ + ch1->cmux = circuitmux_alloc(); + /* Try to register it */ + channel_register(ch1); + tt_assert(ch1->registered); + + /* It should start off in SCHED_CHAN_IDLE */ + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_IDLE); + + /* Now get another one */ + ch2 = new_fake_channel(); + tt_assert(ch2); + ch2->state = CHANNEL_STATE_OPENING; + ch2->cmux = circuitmux_alloc(); + channel_register(ch2); + tt_assert(ch2->registered); + + /* Send it to SCHED_CHAN_WAITING_TO_WRITE */ + scheduler_channel_has_waiting_cells(ch1); + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_WAITING_TO_WRITE); + + /* This should send it to SCHED_CHAN_PENDING */ + scheduler_channel_wants_writes(ch1); + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(channels_pending), ==, 1); + + /* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */ + scheduler_channel_wants_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS); + + /* Drop ch2 back to idle */ + scheduler_channel_doesnt_want_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_IDLE); + + /* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */ + scheduler_channel_wants_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS); + + /* ...and this should kick ch2 into SCHED_CHAN_PENDING */ + scheduler_channel_has_waiting_cells(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(channels_pending), ==, 2); + + /* This should send ch2 to SCHED_CHAN_WAITING_TO_WRITE */ + scheduler_channel_doesnt_want_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_TO_WRITE); + tt_int_op(smartlist_len(channels_pending), ==, 1); + + /* ...and back to SCHED_CHAN_PENDING */ + scheduler_channel_wants_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(channels_pending), ==, 2); + + /* Now we exercise scheduler_touch_channel */ + old_count = scheduler_compare_channels_mock_ctr; + scheduler_touch_channel(ch1); + tt_assert(scheduler_compare_channels_mock_ctr > old_count); + + /* Close */ + channel_mark_for_close(ch1); + tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSING); + channel_mark_for_close(ch2); + tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSING); + channel_closed(ch1); + tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSED); + ch1 = NULL; + channel_closed(ch2); + tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSED); + ch2 = NULL; + + /* Shut things down */ + + channel_free_all(); + scheduler_free_all(); + mock_event_free_all(); + + done: + tor_free(ch1); + tor_free(ch2); + + UNMOCK(scheduler_compare_channels); + UNMOCK(scheduler_run); + UNMOCK(tor_libevent_get_base); + + return; +} + +static void +test_scheduler_compare_channels(void *arg) +{ + /* We don't actually need whole fake channels... */ + channel_t c1, c2; + /* ...and some dummy circuitmuxes too */ + circuitmux_t *cm1 = NULL, *cm2 = NULL; + int result; + + (void)arg; + + /* We can't actually see sizeof(circuitmux_t) from here */ + cm1 = tor_malloc_zero(sizeof(void *)); + cm2 = tor_malloc_zero(sizeof(void *)); + + c1.cmux = cm1; + c2.cmux = cm2; + + /* Configure circuitmux_get_policy() mock */ + mock_cgp_tgt_1 = cm1; + /* + * This is to test the different-policies case, which uses the policy + * cast to an intptr_t as an arbitrary but definite thing to compare. + */ + mock_cgp_val_1 = (const circuitmux_policy_t *)(1); + mock_cgp_tgt_2 = cm2; + mock_cgp_val_2 = (const circuitmux_policy_t *)(2); + + MOCK(circuitmux_get_policy, circuitmux_get_policy_mock); + + /* Now set up circuitmux_compare_muxes() mock using cm1/cm2 */ + mock_ccm_tgt_1 = cm1; + mock_ccm_tgt_2 = cm2; + MOCK(circuitmux_compare_muxes, circuitmux_compare_muxes_mock); + + /* Equal-channel case */ + result = scheduler_compare_channels(&c1, &c1); + tt_int_op(result, ==, 0); + + /* Distinct channels, distinct policies */ + result = scheduler_compare_channels(&c1, &c2); + tt_int_op(result, ==, -1); + result = scheduler_compare_channels(&c2, &c1); + tt_int_op(result, ==, 1); + + /* Distinct channels, same policy */ + mock_cgp_val_2 = mock_cgp_val_1; + result = scheduler_compare_channels(&c1, &c2); + tt_int_op(result, ==, -1); + result = scheduler_compare_channels(&c2, &c1); + tt_int_op(result, ==, 1); + + done: + + UNMOCK(circuitmux_compare_muxes); + mock_ccm_tgt_1 = NULL; + mock_ccm_tgt_2 = NULL; + + UNMOCK(circuitmux_get_policy); + mock_cgp_tgt_1 = NULL; + mock_cgp_val_1 = NULL; + mock_cgp_tgt_2 = NULL; + mock_cgp_val_2 = NULL; + + tor_free(cm1); + tor_free(cm2); + + return; +} + +static void +test_scheduler_initfree(void *arg) +{ + (void)arg; + + tt_ptr_op(channels_pending, ==, NULL); + tt_ptr_op(run_sched_ev, ==, NULL); + + mock_event_init(); + MOCK(tor_libevent_get_base, tor_libevent_get_base_mock); + + scheduler_init(); + + tt_assert(channels_pending != NULL); + tt_assert(run_sched_ev != NULL); + + scheduler_free_all(); + + UNMOCK(tor_libevent_get_base); + mock_event_free_all(); + + tt_ptr_op(channels_pending, ==, NULL); + tt_ptr_op(run_sched_ev, ==, NULL); + + done: + return; +} + +static void +test_scheduler_loop(void *arg) +{ + channel_t *ch1 = NULL, *ch2 = NULL; + + (void)arg; + + /* Set up libevent and scheduler */ + + mock_event_init(); + MOCK(tor_libevent_get_base, tor_libevent_get_base_mock); + scheduler_init(); + /* + * Install the compare channels mock so we can test + * scheduler_touch_channel(). + */ + MOCK(scheduler_compare_channels, scheduler_compare_channels_mock); + /* + * Disable scheduler_run so we can just check the state transitions + * without having to make everything it might call work too. + */ + MOCK(scheduler_run, scheduler_run_noop_mock); + + tt_int_op(smartlist_len(channels_pending), ==, 0); + + /* Set up a fake channel */ + ch1 = new_fake_channel(); + tt_assert(ch1); + + /* Start it off in OPENING */ + ch1->state = CHANNEL_STATE_OPENING; + /* We'll need a cmux */ + ch1->cmux = circuitmux_alloc(); + /* Try to register it */ + channel_register(ch1); + tt_assert(ch1->registered); + /* Finish opening it */ + channel_change_state(ch1, CHANNEL_STATE_OPEN); + + /* It should start off in SCHED_CHAN_IDLE */ + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_IDLE); + + /* Now get another one */ + ch2 = new_fake_channel(); + tt_assert(ch2); + ch2->state = CHANNEL_STATE_OPENING; + ch2->cmux = circuitmux_alloc(); + channel_register(ch2); + tt_assert(ch2->registered); + /* + * Don't open ch2; then channel_num_cells_writeable() will return + * zero and we'll get coverage of that exception case in scheduler_run() + */ + + tt_int_op(ch1->state, ==, CHANNEL_STATE_OPEN); + tt_int_op(ch2->state, ==, CHANNEL_STATE_OPENING); + + /* Send it to SCHED_CHAN_WAITING_TO_WRITE */ + scheduler_channel_has_waiting_cells(ch1); + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_WAITING_TO_WRITE); + + /* This should send it to SCHED_CHAN_PENDING */ + scheduler_channel_wants_writes(ch1); + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(channels_pending), ==, 1); + + /* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */ + scheduler_channel_wants_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS); + + /* Drop ch2 back to idle */ + scheduler_channel_doesnt_want_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_IDLE); + + /* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */ + scheduler_channel_wants_writes(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS); + + /* ...and this should kick ch2 into SCHED_CHAN_PENDING */ + scheduler_channel_has_waiting_cells(ch2); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(channels_pending), ==, 2); + + /* + * Now we've got two pending channels and need to fire off + * scheduler_run(); first, unmock it. + */ + + UNMOCK(scheduler_run); + + scheduler_run(); + + /* Now re-mock it */ + MOCK(scheduler_run, scheduler_run_noop_mock); + + /* + * Assert that they're still in the states we left and aren't still + * pending + */ + tt_int_op(ch1->state, ==, CHANNEL_STATE_OPEN); + tt_int_op(ch2->state, ==, CHANNEL_STATE_OPENING); + tt_assert(ch1->scheduler_state != SCHED_CHAN_PENDING); + tt_assert(ch2->scheduler_state != SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(channels_pending), ==, 0); + + /* Now, finish opening ch2, and get both back to pending */ + channel_change_state(ch2, CHANNEL_STATE_OPEN); + scheduler_channel_wants_writes(ch1); + scheduler_channel_wants_writes(ch2); + scheduler_channel_has_waiting_cells(ch1); + scheduler_channel_has_waiting_cells(ch2); + tt_int_op(ch1->state, ==, CHANNEL_STATE_OPEN); + tt_int_op(ch2->state, ==, CHANNEL_STATE_OPEN); + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_PENDING); + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING); + tt_int_op(smartlist_len(channels_pending), ==, 2); + + /* Now, set up the channel_flush_some_cells() mock */ + MOCK(channel_flush_some_cells, channel_flush_some_cells_mock); + /* + * 16 cells on ch1 means it'll completely drain into the 32 cells + * fakechan's num_cells_writeable() returns. + */ + channel_flush_some_cells_mock_set(ch1, 16); + /* + * This one should get sent back to pending, since num_cells_writeable() + * will still return non-zero. + */ + channel_flush_some_cells_mock_set(ch2, 48); + + /* + * And re-run the scheduler_run() loop with non-zero returns from + * channel_flush_some_cells() this time. + */ + UNMOCK(scheduler_run); + + scheduler_run(); + + /* Now re-mock it */ + MOCK(scheduler_run, scheduler_run_noop_mock); + + /* + * ch1 should have gone to SCHED_CHAN_WAITING_FOR_CELLS, with 16 flushed + * and 32 writeable. + */ + tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS); + /* + * ...ch2 should also have gone to SCHED_CHAN_WAITING_FOR_CELLS, with + * channel_more_to_flush() returning false and channel_num_cells_writeable() + * > 0/ + */ + tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS); + + /* Close */ + channel_mark_for_close(ch1); + tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSING); + channel_mark_for_close(ch2); + tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSING); + channel_closed(ch1); + tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSED); + ch1 = NULL; + channel_closed(ch2); + tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSED); + ch2 = NULL; + + /* Shut things down */ + channel_flush_some_cells_mock_free_all(); + channel_free_all(); + scheduler_free_all(); + mock_event_free_all(); + + done: + tor_free(ch1); + tor_free(ch2); + + UNMOCK(channel_flush_some_cells); + UNMOCK(scheduler_compare_channels); + UNMOCK(scheduler_run); + UNMOCK(tor_libevent_get_base); +} + +static void +test_scheduler_queue_heuristic(void *arg) +{ + time_t now = approx_time(); + uint64_t qh; + + (void)arg; + + queue_heuristic = 0; + queue_heuristic_timestamp = 0; + + /* Not yet inited case */ + scheduler_update_queue_heuristic(now - 180); + tt_u64_op(queue_heuristic, ==, 0); + tt_int_op(queue_heuristic_timestamp, ==, now - 180); + + queue_heuristic = 1000000000L; + queue_heuristic_timestamp = now - 120; + + scheduler_update_queue_heuristic(now - 119); + tt_u64_op(queue_heuristic, ==, 500000000L); + tt_int_op(queue_heuristic_timestamp, ==, now - 119); + + scheduler_update_queue_heuristic(now - 116); + tt_u64_op(queue_heuristic, ==, 62500000L); + tt_int_op(queue_heuristic_timestamp, ==, now - 116); + + qh = scheduler_get_queue_heuristic(); + tt_u64_op(qh, ==, 0); + + done: + return; +} + +struct testcase_t scheduler_tests[] = { + { "channel_states", test_scheduler_channel_states, TT_FORK, NULL, NULL }, + { "compare_channels", test_scheduler_compare_channels, + TT_FORK, NULL, NULL }, + { "initfree", test_scheduler_initfree, TT_FORK, NULL, NULL }, + { "loop", test_scheduler_loop, TT_FORK, NULL, NULL }, + { "queue_heuristic", test_scheduler_queue_heuristic, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_socks.c b/src/test/test_socks.c index fbb8b25980..465e427930 100644 --- a/src/test/test_socks.c +++ b/src/test/test_socks.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" diff --git a/src/test/test_util.c b/src/test/test_util.c index 94671f9430..4891356820 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2014, The Tor Project, Inc. */ + * Copyright (c) 2007-2015, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "orconfig.h" @@ -1822,7 +1822,7 @@ test_util_gzip(void *arg) tor_free(buf1); tor_free(buf2); tor_free(buf3); - state = tor_zlib_new(1, ZLIB_METHOD); + state = tor_zlib_new(1, ZLIB_METHOD, HIGH_COMPRESSION); tt_assert(state); cp1 = buf1 = tor_malloc(1024); len1 = 1024; @@ -4621,6 +4621,58 @@ test_util_round_to_next_multiple_of(void *arg) tt_assert(round_uint64_to_next_multiple_of(99,7) == 105); tt_assert(round_uint64_to_next_multiple_of(99,9) == 99); + tt_assert(round_int64_to_next_multiple_of(0,1) == 0); + tt_assert(round_int64_to_next_multiple_of(0,7) == 0); + + tt_assert(round_int64_to_next_multiple_of(99,1) == 99); + tt_assert(round_int64_to_next_multiple_of(99,7) == 105); + tt_assert(round_int64_to_next_multiple_of(99,9) == 99); + + tt_assert(round_int64_to_next_multiple_of(-99,1) == -99); + tt_assert(round_int64_to_next_multiple_of(-99,7) == -98); + tt_assert(round_int64_to_next_multiple_of(-99,9) == -99); + + tt_assert(round_int64_to_next_multiple_of(INT64_MIN,2) == INT64_MIN); + tt_assert(round_int64_to_next_multiple_of(INT64_MAX,2) == + INT64_MAX-INT64_MAX%2); + done: + ; +} + +static void +test_util_laplace(void *arg) +{ + /* Sample values produced using Python's SciPy: + * + * >>> from scipy.stats import laplace + * >>> laplace.ppf([-0.01, 0.0, 0.01, 0.5, 0.51, 0.99, 1.0, 1.01], + ... loc = 24, scale = 24) + * array([ nan, -inf, -69.88855213, 24. , + * 24.48486498, 117.88855213, inf, nan]) + */ + const double mu = 24.0, b = 24.0; + const double delta_f = 15.0, epsilon = 0.3; /* b = 15.0 / 0.3 = 50.0 */ + (void)arg; + + tt_assert(isinf(sample_laplace_distribution(mu, b, 0.0))); + test_feq(-69.88855213, sample_laplace_distribution(mu, b, 0.01)); + test_feq(24.0, sample_laplace_distribution(mu, b, 0.5)); + test_feq(24.48486498, sample_laplace_distribution(mu, b, 0.51)); + test_feq(117.88855213, sample_laplace_distribution(mu, b, 0.99)); + + /* >>> laplace.ppf([0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99], + * ... loc = 0, scale = 50) + * array([ -inf, -80.47189562, -34.65735903, 0. , + * 34.65735903, 80.47189562, 195.60115027]) + */ + tt_assert(INT64_MIN + 20 == + add_laplace_noise(20, 0.0, delta_f, epsilon)); + tt_assert(-60 == add_laplace_noise(20, 0.1, delta_f, epsilon)); + tt_assert(-14 == add_laplace_noise(20, 0.25, delta_f, epsilon)); + tt_assert(20 == add_laplace_noise(20, 0.5, delta_f, epsilon)); + tt_assert(54 == add_laplace_noise(20, 0.75, delta_f, epsilon)); + tt_assert(100 == add_laplace_noise(20, 0.9, delta_f, epsilon)); + tt_assert(215 == add_laplace_noise(20, 0.99, delta_f, epsilon)); done: ; } @@ -4882,6 +4934,7 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(strtok), UTIL_LEGACY(di_ops), UTIL_TEST(round_to_next_multiple_of, 0), + UTIL_TEST(laplace, 0), UTIL_TEST(strclear, 0), UTIL_TEST(find_str_at_start_of_line, 0), UTIL_TEST(string_is_C_identifier, 0), |