|
@@ -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
|
|
|
+};
|
|
|
+
|