All checks were successful
CI Code / Check spelling (pull_request) Successful in 20s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Code Coverage (pull_request) Successful in 4m54s
CI Code / Linux (ubuntu) (pull_request) Successful in 6m23s
CI Code / Linux (debian) (pull_request) Successful in 9m12s
CI Code / Linux (arch) (pull_request) Successful in 11m12s
- Add 15 unit tests for autoping logic (XEP-0199) - Add 10 helper tests for refactoring validation - Add Last Activity functional tests (XEP-0012) - Add MUC affiliation/kick tests (XEP-0045)
442 lines
9.8 KiB
C
442 lines
9.8 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <glib.h>
|
|
|
|
/*
|
|
* 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);
|
|
}
|