diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index e40df16f2d..650d4f1015 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -41,6 +41,8 @@ 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; /* Setup for mock event stuff */ static void mock_event_free_all(void); @@ -51,9 +53,13 @@ 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_queue_heuristic(void *arg); @@ -130,8 +136,7 @@ circuitmux_compare_muxes_mock(circuitmux_t *cmux_1, 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 { + } else { if (cmux_1 == mock_ccm_tgt_1 || cmux_1 == mock_ccm_tgt_1) result = -1; else if (cmux_2 == mock_ccm_tgt_1 || cmux_2 == mock_ccm_tgt_2) { result = 1; @@ -162,6 +167,28 @@ circuitmux_get_policy_mock(circuitmux_t *cmux) 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) { @@ -170,6 +197,125 @@ tor_libevent_get_base_mock(void) /* 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); + + test_eq(smartlist_len(channels_pending), 0); + + /* Set up a fake channel */ + ch1 = new_fake_channel(); + test_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); + test_assert(ch1->registered); + + /* It should start off in SCHED_CHAN_IDLE */ + test_eq(ch1->scheduler_state, SCHED_CHAN_IDLE); + + /* Now get another one */ + ch2 = new_fake_channel(); + test_assert(ch2); + ch2->state = CHANNEL_STATE_OPENING; + ch2->cmux = circuitmux_alloc(); + channel_register(ch2); + test_assert(ch2->registered); + + /* Send it to SCHED_CHAN_WAITING_TO_WRITE */ + scheduler_channel_has_waiting_cells(ch1); + test_eq(ch1->scheduler_state, SCHED_CHAN_WAITING_TO_WRITE); + + /* This should send it to SCHED_CHAN_PENDING */ + scheduler_channel_wants_writes(ch1); + test_eq(ch1->scheduler_state, SCHED_CHAN_PENDING); + test_eq(smartlist_len(channels_pending), 1); + + /* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */ + scheduler_channel_wants_writes(ch2); + test_eq(ch2->scheduler_state, SCHED_CHAN_WAITING_FOR_CELLS); + + /* Drop ch2 back to idle */ + scheduler_channel_doesnt_want_writes(ch2); + test_eq(ch2->scheduler_state, SCHED_CHAN_IDLE); + + /* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */ + scheduler_channel_wants_writes(ch2); + test_eq(ch2->scheduler_state, SCHED_CHAN_WAITING_FOR_CELLS); + + /* ...and this should kick ch2 into SCHED_CHAN_PENDING */ + scheduler_channel_has_waiting_cells(ch2); + test_eq(ch2->scheduler_state, SCHED_CHAN_PENDING); + test_eq(smartlist_len(channels_pending), 2); + + /* This should send ch2 to SCHED_CHAN_WAITING_TO_WRITE */ + scheduler_channel_doesnt_want_writes(ch2); + test_eq(ch2->scheduler_state, SCHED_CHAN_WAITING_TO_WRITE); + test_eq(smartlist_len(channels_pending), 1); + + /* ...and back to SCHED_CHAN_PENDING */ + scheduler_channel_wants_writes(ch2); + test_eq(ch2->scheduler_state, SCHED_CHAN_PENDING); + test_eq(smartlist_len(channels_pending), 2); + + /* Now we exercise scheduler_touch_channel */ + old_count = scheduler_compare_channels_mock_ctr; + scheduler_touch_channel(ch1); + test_assert(scheduler_compare_channels_mock_ctr > old_count); + + /* Close */ + channel_mark_for_close(ch1); + test_eq(ch1->state, CHANNEL_STATE_CLOSING); + channel_mark_for_close(ch2); + test_eq(ch2->state, CHANNEL_STATE_CLOSING); + channel_closed(ch1); + test_eq(ch1->state, CHANNEL_STATE_CLOSED); + ch1 = NULL; + channel_closed(ch2); + test_eq(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) { @@ -208,7 +354,7 @@ test_scheduler_compare_channels(void *arg) /* Equal-channel case */ result = scheduler_compare_channels(&c1, &c1); test_eq(result, 0); - + /* Distinct channels, distinct policies */ result = scheduler_compare_channels(&c1, &c2); test_eq(result, -1); @@ -303,6 +449,7 @@ test_scheduler_queue_heuristic(void *arg) } 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 },