From b21aeaa6f88cb006bab4ec5b9f184c1b73e0c2d5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 19 Oct 2026 14:59:55 +0200 Subject: [PATCH] Test normal call busy confirmation and all incoming rejection policies --- tests/test_call.c | 66 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/tests/test_call.c b/tests/test_call.c index d9a3eeb8..d286ce75 100644 --- a/tests/test_call.c +++ b/tests/test_call.c @@ -79,6 +79,7 @@ enum call_phase { P_DECLINE, /* B: call_decline */ P_WAIT_ENDED2, /* обе ENDED + A DECLINED */ P_TIMEOUT_START, P_TIMEOUT_INCOMING, P_TIMEOUT_ACTIVE, P_TIMEOUT_ENDED, + P_POLICY_START, P_POLICY_WAIT, P_POLICY_RING, P_POLICY_END, P_DONE, }; @@ -93,11 +94,12 @@ struct call_test_ctx { uint64_t call_id; /* текущий call_id */ uint64_t previous_call_id; int video_restart_tested; + int policy_index; }; /* записи событий CHAT_EVT_CALL_* на узел */ struct call_evt { - int incoming, ringing, accepted, ended, declined, errors; + int incoming, ringing, accepted, ended, declined, errors, phone_busy; uint64_t route_peer, route_group; uint64_t incoming_call_id; uint64_t accepted_call_id; @@ -318,6 +320,9 @@ static void call_evt_handler(struct UTUN_INSTANCE* inst, int type, const uint8_t struct call_evt* ev = (struct call_evt*)inst->test_user_ptr; if (!ev) return; switch (type) { + case CHAT_EVT_CALL_PHONE_BUSY: + ev->phone_busy++; + break; case CHAT_EVT_CALL_ERROR: ev->errors++; break; @@ -863,8 +868,65 @@ static void call_tick(void* arg) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "unconfirmed end did not release sessions cleanly"); t->result = 2; break; } DEBUG_INFO(DEBUG_CATEGORY_CALL, "call end regression: epoch reset, ACK loss, stale end, simultaneous end and deadline passed"); - t->phase = P_DONE; + t->phase = P_POLICY_START; break; + case P_POLICY_START: { + static const struct { int state; unsigned policy; } policies[] = { + {0, CALL_POLICY_WARN_PHONE}, {2, CALL_POLICY_PUBLISH_PHONE}, {2, CALL_POLICY_WARN_PHONE}, + {2, CALL_POLICY_WARN_PHONE}, {2, CALL_POLICY_WARN_PHONE | CALL_POLICY_REJECT_PHONE}, + {0, CALL_POLICY_BLOCK_CALLS}, {2, CALL_POLICY_BLOCK_CALLS | CALL_POLICY_WARN_PHONE}, + {2, CALL_POLICY_BLOCK_RADIO}, {2, CALL_POLICY_WARN_PHONE}, {2, CALL_POLICY_WARN_PHONE} + }; + if (t->policy_index == (int)(sizeof(policies) / sizeof(policies[0]))) { + call_set_phone_state(B, 0, CALL_POLICY_PUBLISH_PHONE); + DEBUG_INFO(DEBUG_CATEGORY_CALL, "phone policy passed: independent flags, no incoming UI before choice, override, cancel, rejection, deadline"); + t->phase = P_DONE; break; + } + memset(ea, 0, sizeof(*ea)); memset(eb, 0, sizeof(*eb)); + call_set_phone_state(B, policies[t->policy_index].state, policies[t->policy_index].policy); + ++t->call_id; + call_start(A, CH_ID, nb, t->call_id); + t->phase = P_POLICY_WAIT; break; + } + case P_POLICY_WAIT: + if (t->policy_index == 4 || t->policy_index == 5 || t->policy_index == 6) { + if (!ea->ended) break; + int expected = t->policy_index == 4 ? CALL_REASON_PHONE_BUSY : CALL_REASON_DECLINE; + if (eb->incoming || ea->phone_busy || ea->ended_reason != expected) goto policy_failed; + t->phase = P_POLICY_END; break; + } + if (t->policy_index == 2 || t->policy_index == 3 || t->policy_index >= 8) { + if (!ea->phone_busy) break; + if (eb->incoming || ea->ringing || ea->phone_busy != 1) goto policy_failed; + if (t->policy_index == 2) { + call_hangup(A, t->call_id); t->phase = P_POLICY_END; + } else if (t->policy_index == 8) { + struct call_session* s = call_session_find(A->call, t->call_id); + s->started_tb -= CALL_RING_TIMEOUT_TB; + call_continue(A, t->call_id); + if (ea->ended_reason != CALL_REASON_RING_TIMEOUT) goto policy_failed; + t->phase = P_POLICY_END; + } else { + if (t->policy_index == 9) call_set_phone_state(B, 2, CALL_POLICY_BLOCK_CALLS | CALL_POLICY_WARN_PHONE); + call_continue(A, t->call_id); + t->phase = t->policy_index == 9 ? P_POLICY_END : P_POLICY_RING; + } + } else t->phase = P_POLICY_RING; + break; + case P_POLICY_RING: + if (!eb->incoming) break; + if (eb->incoming_call_id != t->call_id || (t->policy_index != 3 && ea->phone_busy)) goto policy_failed; + call_decline(B, t->call_id); t->phase = P_POLICY_END; break; + case P_POLICY_END: + if (!ea->ended || !eb->ended || call_session_find(A->call, t->call_id) || call_session_find(B->call, t->call_id)) break; + if (ea->ended != 1 || eb->ended != 1 || (t->policy_index == 9 && (eb->incoming || ea->ended_reason != CALL_REASON_DECLINE))) + goto policy_failed; + call_continue(A, t->call_id); // Поздняя команда не должна создавать новую сессию. + ++t->policy_index; t->phase = P_POLICY_START; break; + policy_failed: + DEBUG_ERROR(DEBUG_CATEGORY_CALL, "phone policy failed scenario=%d incoming=%d busy_events=%d reason=%u", + t->policy_index, eb->incoming, ea->phone_busy, ea->ended_reason); + t->result = 2; break; case P_DONE: t->result = 1; uasync_stop(t->ua);