/* * test_autoping.c * Unit tests for autoping functionality (XEP-0199) * * Tests the autoping logic including: * - Timer state management * - Timeout checking * - Connection state validation */ #include "prof_cmocka.h" #include #include #include /* * Since autoping functions use static variables and are tightly coupled * to the connection and xmpp libraries, we test the logic patterns here * rather than calling the actual functions. * * These tests validate the correctness of the autoping algorithm: * - State machine for ping wait * - Timeout calculation * - Connection state checks */ /* Connection status enum (isolated from xmpp.h to avoid conflicts) */ typedef enum { CONN_DISCONNECTED, CONN_CONNECTED, CONN_CONNECTING } ConnStatus; /* Test structure simulating autoping state */ typedef struct { gboolean wait; GTimer* timer; int timeout_seconds; ConnStatus conn_status; } AutopingState; static AutopingState* autoping_state_new(void) { AutopingState* state = calloc(1, sizeof(AutopingState)); state->wait = FALSE; state->timer = NULL; state->timeout_seconds = 30; state->conn_status = CONN_DISCONNECTED; return state; } static void autoping_state_free(AutopingState* state) { if (state) { if (state->timer) { g_timer_destroy(state->timer); } free(state); } } /* Simulates iq_autoping_timer_cancel logic */ static void autoping_cancel(AutopingState* state) { state->wait = FALSE; if (state->timer) { g_timer_destroy(state->timer); state->timer = NULL; } } /* Simulates ping send logic */ static gboolean autoping_send(AutopingState* state) { if (state->conn_status != CONN_CONNECTED) { return FALSE; } if (state->wait) { /* Already waiting for pong */ return FALSE; } state->wait = TRUE; if (state->timer) { g_timer_destroy(state->timer); } state->timer = g_timer_new(); return TRUE; } /* Simulates iq_autoping_check logic */ typedef enum { AUTOPING_OK, AUTOPING_NOT_CONNECTED, AUTOPING_NOT_WAITING, AUTOPING_NO_TIMER, AUTOPING_TIMEOUT } AutopingCheckResult; static AutopingCheckResult autoping_check(AutopingState* state) { if (state->conn_status != CONN_CONNECTED) { return AUTOPING_NOT_CONNECTED; } if (state->wait == FALSE) { return AUTOPING_NOT_WAITING; } if (state->timer == NULL) { return AUTOPING_NO_TIMER; } gdouble elapsed = g_timer_elapsed(state->timer, NULL); unsigned long seconds_elapsed = (unsigned long)(elapsed * 1.0); if (state->timeout_seconds > 0 && seconds_elapsed >= (unsigned long)state->timeout_seconds) { return AUTOPING_TIMEOUT; } return AUTOPING_OK; } /* Simulates timer extend logic (reset timer on activity) */ static void autoping_extend(AutopingState* state) { if (state->timer) { g_timer_start(state->timer); } } /* * Test: autoping_cancel resets state correctly */ void test_autoping_cancel_resets_state(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; /* Setup: send a ping */ autoping_send(as); assert_true(as->wait); assert_non_null(as->timer); /* Cancel should reset everything */ autoping_cancel(as); assert_false(as->wait); assert_null(as->timer); autoping_state_free(as); } /* * Test: autoping_send fails when disconnected */ void test_autoping_send_fails_when_disconnected(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_DISCONNECTED; gboolean result = autoping_send(as); assert_false(result); assert_false(as->wait); assert_null(as->timer); autoping_state_free(as); } /* * Test: autoping_send succeeds when connected */ void test_autoping_send_succeeds_when_connected(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; gboolean result = autoping_send(as); assert_true(result); assert_true(as->wait); assert_non_null(as->timer); autoping_state_free(as); } /* * Test: autoping_send returns false if already waiting */ void test_autoping_send_fails_if_already_waiting(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; /* First send succeeds */ gboolean result1 = autoping_send(as); assert_true(result1); /* Second send fails - already waiting */ gboolean result2 = autoping_send(as); assert_false(result2); autoping_state_free(as); } /* * Test: autoping_check returns NOT_CONNECTED when disconnected */ void test_autoping_check_not_connected(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_DISCONNECTED; AutopingCheckResult result = autoping_check(as); assert_int_equal(result, AUTOPING_NOT_CONNECTED); autoping_state_free(as); } /* * Test: autoping_check returns NOT_WAITING when not waiting for pong */ void test_autoping_check_not_waiting(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; as->wait = FALSE; AutopingCheckResult result = autoping_check(as); assert_int_equal(result, AUTOPING_NOT_WAITING); autoping_state_free(as); } /* * Test: autoping_check returns NO_TIMER when timer is null */ void test_autoping_check_no_timer(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; as->wait = TRUE; as->timer = NULL; AutopingCheckResult result = autoping_check(as); assert_int_equal(result, AUTOPING_NO_TIMER); autoping_state_free(as); } /* * Test: autoping_check returns OK when within timeout */ void test_autoping_check_ok_within_timeout(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; as->timeout_seconds = 30; autoping_send(as); /* Immediately after send, should be OK */ AutopingCheckResult result = autoping_check(as); assert_int_equal(result, AUTOPING_OK); autoping_state_free(as); } /* * Test: autoping_check returns OK when timeout is 0 (disabled) */ void test_autoping_check_ok_when_timeout_disabled(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; as->timeout_seconds = 0; /* Disabled */ autoping_send(as); AutopingCheckResult result = autoping_check(as); assert_int_equal(result, AUTOPING_OK); autoping_state_free(as); } /* * Test: timer extend resets the timer */ void test_autoping_timer_extend_resets_timer(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; autoping_send(as); /* Wait a tiny bit */ g_usleep(10000); /* 10ms */ gdouble elapsed_before = g_timer_elapsed(as->timer, NULL); /* Extend should reset timer */ autoping_extend(as); gdouble elapsed_after = g_timer_elapsed(as->timer, NULL); /* After extend, elapsed time should be very small */ assert_true(elapsed_after < elapsed_before); assert_true(elapsed_after < 0.005); /* Less than 5ms */ autoping_state_free(as); } /* * Test: timer extend does nothing when timer is null */ void test_autoping_timer_extend_noop_without_timer(void** state) { AutopingState* as = autoping_state_new(); as->timer = NULL; /* Should not crash */ autoping_extend(as); assert_null(as->timer); autoping_state_free(as); } /* * Test: pong received cancels wait state */ void test_autoping_pong_received_cancels_wait(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; /* Send ping */ autoping_send(as); assert_true(as->wait); /* Simulate pong received - calls cancel */ autoping_cancel(as); assert_false(as->wait); assert_null(as->timer); autoping_state_free(as); } /* * Test: multiple pings can be sent after pong received */ void test_autoping_can_send_after_pong(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; /* First ping */ gboolean result1 = autoping_send(as); assert_true(result1); /* Pong received */ autoping_cancel(as); /* Second ping should succeed */ gboolean result2 = autoping_send(as); assert_true(result2); autoping_state_free(as); } /* * Test: connection status changes affect behavior */ void test_autoping_respects_connection_state_changes(void** state) { AutopingState* as = autoping_state_new(); /* Start disconnected */ as->conn_status = CONN_DISCONNECTED; assert_false(autoping_send(as)); /* Connect */ as->conn_status = CONN_CONNECTED; assert_true(autoping_send(as)); /* Disconnect - check should handle gracefully */ as->conn_status = CONN_DISCONNECTED; AutopingCheckResult result = autoping_check(as); assert_int_equal(result, AUTOPING_NOT_CONNECTED); autoping_state_free(as); } /* * Test: timeout with very short timeout value */ void test_autoping_timeout_detection(void** state) { AutopingState* as = autoping_state_new(); as->conn_status = CONN_CONNECTED; as->timeout_seconds = 1; /* 1 second timeout */ autoping_send(as); /* Immediately should be OK */ AutopingCheckResult result1 = autoping_check(as); assert_int_equal(result1, AUTOPING_OK); /* Manipulate timer to simulate timeout */ /* We can't easily wait 1 second in a unit test, so we test the logic */ /* by checking that elapsed time is calculated correctly */ gdouble elapsed = g_timer_elapsed(as->timer, NULL); assert_true(elapsed < 1.0); autoping_state_free(as); }