From 6d886787e3e112693dc8ac9cda021c93eaefee8b Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 13 Dec 2013 06:34:46 -0800 Subject: [PATCH] Unit tests for channel_get_cell_queue_entry_size() and channel_write_*() functions --- src/test/Makefile.nmake | 2 +- src/test/include.am | 1 + src/test/test.c | 3 + src/test/test_channel.c | 295 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 src/test/test_channel.c diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake index 822431f3b8..fceef99564 100644 --- a/src/test/Makefile.nmake +++ b/src/test/Makefile.nmake @@ -11,7 +11,7 @@ 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_containers.obj \ test_controller_events.ogj test_crypto.obj test_data.obj test_dir.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 \ diff --git a/src/test/include.am b/src/test/include.am index 77c92f12f8..547f23b733 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -20,6 +20,7 @@ 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_circuitlist.c \ src/test/test_circuitmux.c \ src/test/test_containers.c \ diff --git a/src/test/test.c b/src/test/test.c index cfbe203d2e..4d4f6f23a4 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1308,6 +1308,8 @@ extern struct testcase_t oom_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[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -1340,6 +1342,7 @@ static struct testgroup_t testgroups[] = { { "policy/" , policy_tests }, { "status/" , status_tests }, { "routerset/" , routerset_tests }, + { "channel/", channel_tests }, END_OF_GROUPS }; diff --git a/src/test/test_channel.c b/src/test/test_channel.c new file mode 100644 index 0000000000..c4241db77e --- /dev/null +++ b/src/test/test_channel.c @@ -0,0 +1,295 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define TOR_CHANNEL_INTERNAL_ +#include "or.h" +#include "channel.h" +/* For channel_note_destroy_not_pending */ +#include "circuitlist.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" +#include "test.h" + +static int test_chan_accept_cells = 0; +static int test_cells_written = 0; +static int test_destroy_not_pending_calls = 0; + +static void channel_note_destroy_not_pending_mock(channel_t *ch, + circid_t circid); +static void chan_test_close(channel_t *ch); +static size_t chan_test_num_bytes_queued(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 make_fake_cell(cell_t *c); +static void make_fake_var_cell(var_cell_t *c); +static channel_t * new_fake_channel(void); +static void scheduler_release_channel_mock(channel_t *ch); +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 void +chan_test_close(channel_t *ch) +{ + test_assert(ch); + + done: + return; +} + +static size_t +chan_test_num_bytes_queued(channel_t *ch) +{ + test_assert(ch); + + done: + return 0; +} + +static int +chan_test_write_cell(channel_t *ch, cell_t *cell) +{ + int rv = 0; + + test_assert(ch); + test_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; + + test_assert(ch); + test_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; + + test_assert(ch); + test_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; +} + +static void +make_fake_cell(cell_t *c) +{ + test_assert(c != NULL); + + c->circ_id = 1; + c->command = CELL_RELAY; + memset(c->payload, 0, CELL_PAYLOAD_SIZE); + + done: + return; +} + +static void +make_fake_var_cell(var_cell_t *c) +{ + test_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; +} + +static channel_t * +new_fake_channel(void) +{ + channel_t *chan = tor_malloc_zero(sizeof(channel_t)); + channel_init(chan); + + chan->close = chan_test_close; + chan->num_bytes_queued = chan_test_num_bytes_queued; + 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; +} + +static void +scheduler_release_channel_mock(channel_t *ch) +{ + (void)ch; + + /* Increment counter */ + ++test_releases_count; + + 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; + + init_cell_pool(); + + packed_cell = packed_cell_new(); + test_assert(packed_cell); + + ch = new_fake_channel(); + test_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); + test_assert(test_cells_written == old_count + 1); + + channel_write_var_cell(ch, var_cell); + test_assert(test_cells_written == old_count + 2); + + channel_write_packed_cell(ch, packed_cell); + test_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); + test_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); + test_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); + test_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); + test_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); + test_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); + test_assert(test_cells_written == old_count); + + packed_cell = packed_cell_new(); + channel_write_packed_cell(ch, packed_cell); + test_assert(test_cells_written == old_count); + + free_cell_pool(); + + done: + tor_free(ch); + + return; +} + +struct testcase_t channel_tests[] = { + { "write", test_channel_write, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; +