diff --git a/tests/test_etcp_lifecycle.c b/tests/test_etcp_lifecycle.c index 69c6cf82..f005c59a 100644 --- a/tests/test_etcp_lifecycle.c +++ b/tests/test_etcp_lifecycle.c @@ -142,6 +142,35 @@ static void ncd_late_up_case(struct UTUN_INSTANCE* inst) { CHECK(!inst->ncd_registry && !inst->connections->count); puts("PASS NCD late UP after timeout and subsequent reconnect"); } +struct link_close_case { struct ETCP_LINK *neighbor; unsigned events, nested; }; +static void close_neighbor(struct ETCP_CONN *conn, struct ETCP_LINK *link, int old_state, int old_status, void *arg) { + (void)conn; (void)old_state; (void)old_status; + struct link_close_case *test = arg; + test->events++; + CHECK(link->closing); + etcp_link_close(link); + if (test->neighbor && test->neighbor != link) { + struct ETCP_LINK *neighbor = test->neighbor; + test->neighbor = NULL; test->nested++; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Closing neighbor from teardown callback: current=%u neighbor=%u", + link->local_link_id, neighbor->local_link_id); + etcp_link_close(neighbor); + } +} +static void neighbor_close_case(struct UTUN_INSTANCE *inst) { + struct ETCP_CONN *conn = etcp_connection_create(inst, "neighbor close"); CHECK(conn); + CHECK(etcp_conn_ref_take(conn) == 0); + CHECK(etcp_link_new(conn, NULL, NULL, 0)); + struct link_close_case test = {.neighbor = etcp_link_new(conn, NULL, NULL, 0)}; CHECK(test.neighbor); + etcp_add_link_status_cbk(inst, close_neighbor, &test); + etcp_connection_close(conn); + CHECK(!conn->links && test.events == 2 && test.nested == 1 && conn->delete_complete); + etcp_remove_link_status_cbk(inst, close_neighbor, &test); + etcp_conn_ref_free(conn); uasync_drain_immediate(inst->ua); uasync_poll(inst->ua, 0); + CHECK(!inst->connections->count); + puts("PASS nested close: current and neighbor links, no cached next pointer"); +} + int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); const char* log = getenv("LIFECYCLE_TEST_LOG"); @@ -173,7 +202,7 @@ int main(void) { uasync_poll(inst.ua,0); CHECK(inst.connections->count == 0); printf("PASS lifecycle scenario %d\n",scenario+1); } - ncd_cases(&inst); ncd_late_up_case(&inst); recovery_cases(&inst); + ncd_cases(&inst); ncd_late_up_case(&inst); recovery_cases(&inst); neighbor_close_case(&inst); queue_free(inst.connections); uasync_poll(inst.ua,0); CHECK(inst.ua->timer_alloc_count == inst.ua->timer_free_count); uasync_destroy(inst.ua,0); CHECK(u_get_allocated_count() == baseline);