summaryrefslogtreecommitdiff
path: root/pjsip/src/test/inv_offer_answer_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src/test/inv_offer_answer_test.c')
-rw-r--r--pjsip/src/test/inv_offer_answer_test.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/pjsip/src/test/inv_offer_answer_test.c b/pjsip/src/test/inv_offer_answer_test.c
new file mode 100644
index 00000000..22809ddf
--- /dev/null
+++ b/pjsip/src/test/inv_offer_answer_test.c
@@ -0,0 +1,677 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "test.h"
+#include <pjsip_ua.h>
+#include <pjsip.h>
+#include <pjlib.h>
+
+#define THIS_FILE "inv_offer_answer_test.c"
+#define PORT 5068
+#define CONTACT "sip:127.0.0.1:5068"
+#define TRACE_(x) PJ_LOG(3,x)
+
+static struct oa_sdp_t
+{
+ const char *offer;
+ const char *answer;
+ unsigned pt_result;
+} oa_sdp[] =
+{
+ {
+ /* Offer: */
+ "v=0\r\n"
+ "o=alice 1 1 IN IP4 host.anywhere.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.anywhere.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49170 RTP/AVP 0\r\n"
+ "a=rtpmap:0 PCMU/8000\r\n",
+
+ /* Answer: */
+ "v=0\r\n"
+ "o=bob 1 1 IN IP4 host.example.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.example.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49920 RTP/AVP 0\r\n"
+ "a=rtpmap:0 PCMU/8000\r\n"
+ "m=video 0 RTP/AVP 31\r\n",
+
+ 0
+ },
+
+ {
+ /* Offer: */
+ "v=0\r\n"
+ "o=alice 2 2 IN IP4 host.anywhere.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.anywhere.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49170 RTP/AVP 8\r\n"
+ "a=rtpmap:0 PCMA/8000\r\n",
+
+ /* Answer: */
+ "v=0\r\n"
+ "o=bob 2 2 IN IP4 host.example.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.example.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49920 RTP/AVP 8\r\n"
+ "a=rtpmap:0 PCMA/8000\r\n",
+
+ 8
+ },
+
+ {
+ /* Offer: */
+ "v=0\r\n"
+ "o=alice 3 3 IN IP4 host.anywhere.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.anywhere.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49170 RTP/AVP 3\r\n",
+
+ /* Answer: */
+ "v=0\r\n"
+ "o=bob 3 3 IN IP4 host.example.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.example.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49920 RTP/AVP 3\r\n",
+
+ 3
+ },
+
+ {
+ /* Offer: */
+ "v=0\r\n"
+ "o=alice 4 4 IN IP4 host.anywhere.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.anywhere.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49170 RTP/AVP 4\r\n",
+
+ /* Answer: */
+ "v=0\r\n"
+ "o=bob 4 4 IN IP4 host.example.com\r\n"
+ "s= \r\n"
+ "c=IN IP4 host.example.com\r\n"
+ "t=0 0\r\n"
+ "m=audio 49920 RTP/AVP 4\r\n",
+
+ 4
+ }
+};
+
+
+
+typedef enum oa_t
+{
+ OFFERER_NONE,
+ OFFERER_UAC,
+ OFFERER_UAS
+} oa_t;
+
+typedef struct inv_test_param_t
+{
+ char *title;
+ unsigned inv_option;
+ pj_bool_t need_established;
+ unsigned count;
+ oa_t oa[4];
+} inv_test_param_t;
+
+typedef struct inv_test_t
+{
+ inv_test_param_t param;
+ pjsip_inv_session *uac;
+ pjsip_inv_session *uas;
+
+ pj_bool_t complete;
+ pj_bool_t uas_complete,
+ uac_complete;
+
+ unsigned oa_index;
+ unsigned uac_update_cnt,
+ uas_update_cnt;
+} inv_test_t;
+
+
+/**************** GLOBALS ******************/
+static inv_test_t inv_test;
+static unsigned job_cnt;
+
+typedef enum job_type
+{
+ SEND_OFFER,
+ ESTABLISH_CALL
+} job_type;
+
+typedef struct job_t
+{
+ job_type type;
+ pjsip_role_e who;
+} job_t;
+
+static job_t jobs[128];
+
+
+/**************** UTILS ******************/
+static pjmedia_sdp_session *create_sdp(pj_pool_t *pool, const char *body)
+{
+ pjmedia_sdp_session *sdp;
+ pj_str_t dup;
+ pj_status_t status;
+
+ pj_strdup2_with_null(pool, &dup, body);
+ status = pjmedia_sdp_parse(pool, dup.ptr, dup.slen, &sdp);
+ pj_assert(status == PJ_SUCCESS);
+
+ return sdp;
+}
+
+/**************** INVITE SESSION CALLBACKS ******************/
+static void on_rx_offer(pjsip_inv_session *inv,
+ const pjmedia_sdp_session *offer)
+{
+ pjmedia_sdp_session *sdp;
+
+ sdp = create_sdp(inv->dlg->pool, oa_sdp[inv_test.oa_index].answer);
+ pjsip_inv_set_sdp_answer(inv, sdp);
+
+ if (inv_test.oa_index == inv_test.param.count-1 &&
+ inv_test.param.need_established)
+ {
+ jobs[job_cnt].type = ESTABLISH_CALL;
+ jobs[job_cnt].who = PJSIP_ROLE_UAS;
+ job_cnt++;
+ }
+}
+
+
+static void on_create_offer(pjsip_inv_session *inv,
+ pjmedia_sdp_session **p_offer)
+{
+ pj_assert(!"Should not happen");
+}
+
+static void on_media_update(pjsip_inv_session *inv_ses,
+ pj_status_t status)
+{
+ if (inv_ses == inv_test.uas) {
+ inv_test.uas_update_cnt++;
+ pj_assert(inv_test.uas_update_cnt - inv_test.uac_update_cnt <= 1);
+ TRACE_((THIS_FILE, " Callee media is established"));
+ } else if (inv_ses == inv_test.uac) {
+ inv_test.uac_update_cnt++;
+ pj_assert(inv_test.uac_update_cnt - inv_test.uas_update_cnt <= 1);
+ TRACE_((THIS_FILE, " Caller media is established"));
+
+ } else {
+ pj_assert(!"Unknown session!");
+ }
+
+ if (inv_test.uac_update_cnt == inv_test.uas_update_cnt) {
+ inv_test.oa_index++;
+
+ if (inv_test.oa_index < inv_test.param.count) {
+ switch (inv_test.param.oa[inv_test.oa_index]) {
+ case OFFERER_UAC:
+ jobs[job_cnt].type = SEND_OFFER;
+ jobs[job_cnt].who = PJSIP_ROLE_UAC;
+ job_cnt++;
+ break;
+ case OFFERER_UAS:
+ jobs[job_cnt].type = SEND_OFFER;
+ jobs[job_cnt].who = PJSIP_ROLE_UAS;
+ job_cnt++;
+ break;
+ default:
+ pj_assert(!"Invalid oa");
+ }
+ }
+
+ pj_assert(job_cnt <= PJ_ARRAY_SIZE(jobs));
+ }
+}
+
+static void on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
+{
+ const char *who = NULL;
+
+ if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+ TRACE_((THIS_FILE, " %s call disconnected",
+ (inv==inv_test.uas ? "Callee" : "Caller")));
+ return;
+ }
+
+ if (inv->state != PJSIP_INV_STATE_CONFIRMED)
+ return;
+
+ if (inv == inv_test.uas) {
+ inv_test.uas_complete = PJ_TRUE;
+ who = "Callee";
+ } else if (inv == inv_test.uac) {
+ inv_test.uac_complete = PJ_TRUE;
+ who = "Caller";
+ } else
+ pj_assert(!"No session");
+
+ TRACE_((THIS_FILE, " %s call is confirmed", who));
+
+ if (inv_test.uac_complete && inv_test.uas_complete)
+ inv_test.complete = PJ_TRUE;
+}
+
+
+/**************** MODULE TO RECEIVE INITIAL INVITE ******************/
+
+static pj_bool_t on_rx_request(pjsip_rx_data *rdata)
+{
+ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG &&
+ rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD)
+ {
+ pjsip_dialog *dlg;
+ pjmedia_sdp_session *sdp = NULL;
+ pj_str_t uri;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ /*
+ * Create UAS
+ */
+ uri = pj_str(CONTACT);
+ status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata,
+ &uri, &dlg);
+ pj_assert(status == PJ_SUCCESS);
+
+ if (inv_test.param.oa[0] == OFFERER_UAC)
+ sdp = create_sdp(rdata->tp_info.pool, oa_sdp[0].answer);
+ else if (inv_test.param.oa[0] == OFFERER_UAS)
+ sdp = create_sdp(rdata->tp_info.pool, oa_sdp[0].offer);
+ else
+ pj_assert(!"Invalid offerer type");
+
+ status = pjsip_inv_create_uas(dlg, rdata, sdp, inv_test.param.inv_option, &inv_test.uas);
+ pj_assert(status == PJ_SUCCESS);
+
+ TRACE_((THIS_FILE, " Sending 183 with SDP"));
+
+ /*
+ * Answer with 183
+ */
+ status = pjsip_inv_initial_answer(inv_test.uas, rdata, 183, NULL,
+ NULL, &tdata);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pjsip_inv_send_msg(inv_test.uas, tdata);
+ pj_assert(status == PJ_SUCCESS);
+
+ return PJ_TRUE;
+ }
+
+ return PJ_FALSE;
+}
+
+static pjsip_module mod_inv_oa_test =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-inv-oa-test", 15 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &on_rx_request, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+};
+
+
+/**************** THE TEST ******************/
+static void run_job(job_t *j)
+{
+ pjsip_inv_session *inv;
+ pjsip_tx_data *tdata;
+ pjmedia_sdp_session *sdp;
+ pj_status_t status;
+
+ if (j->who == PJSIP_ROLE_UAC)
+ inv = inv_test.uac;
+ else
+ inv = inv_test.uas;
+
+ switch (j->type) {
+ case SEND_OFFER:
+ sdp = create_sdp(inv->dlg->pool, oa_sdp[inv_test.oa_index].offer);
+
+ TRACE_((THIS_FILE, " Sending UPDATE with offer"));
+ status = pjsip_inv_update(inv, NULL, sdp, &tdata);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pjsip_inv_send_msg(inv, tdata);
+ pj_assert(status == PJ_SUCCESS);
+ break;
+ case ESTABLISH_CALL:
+ TRACE_((THIS_FILE, " Sending 200/OK"));
+ status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pjsip_inv_send_msg(inv, tdata);
+ pj_assert(status == PJ_SUCCESS);
+ break;
+ }
+}
+
+
+static int perform_test(inv_test_param_t *param)
+{
+ pj_str_t uri;
+ pjsip_dialog *dlg;
+ pjmedia_sdp_session *sdp;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, " %s", param->title));
+
+ pj_bzero(&inv_test, sizeof(inv_test));
+ pj_memcpy(&inv_test.param, param, sizeof(*param));
+ job_cnt = 0;
+
+ uri = pj_str(CONTACT);
+
+ /*
+ * Create UAC
+ */
+ status = pjsip_dlg_create_uac(pjsip_ua_instance(),
+ &uri, &uri, &uri, &uri, &dlg);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, -10);
+
+ if (inv_test.param.oa[0] == OFFERER_UAC)
+ sdp = create_sdp(dlg->pool, oa_sdp[0].offer);
+ else
+ sdp = NULL;
+
+ status = pjsip_inv_create_uac(dlg, sdp, inv_test.param.inv_option, &inv_test.uac);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, -20);
+
+ TRACE_((THIS_FILE, " Sending INVITE %s offer", (sdp ? "with" : "without")));
+
+ /*
+ * Make call!
+ */
+ status = pjsip_inv_invite(inv_test.uac, &tdata);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
+
+ status = pjsip_inv_send_msg(inv_test.uac, tdata);
+ PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30);
+
+ /*
+ * Wait until test completes
+ */
+ while (!inv_test.complete) {
+ pj_time_val delay = {0, 20};
+
+ pjsip_endpt_handle_events(endpt, &delay);
+
+ while (job_cnt) {
+ job_t j;
+
+ j = jobs[0];
+ pj_array_erase(jobs, sizeof(jobs[0]), job_cnt, 0);
+ --job_cnt;
+
+ run_job(&j);
+ }
+ }
+
+ flush_events(100);
+
+ /*
+ * Hangup
+ */
+ TRACE_((THIS_FILE, " Disconnecting call"));
+ status = pjsip_inv_end_session(inv_test.uas, PJSIP_SC_DECLINE, 0, &tdata);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pjsip_inv_send_msg(inv_test.uas, tdata);
+ pj_assert(status == PJ_SUCCESS);
+
+ flush_events(500);
+
+ return 0;
+}
+
+
+static pj_bool_t log_on_rx_msg(pjsip_rx_data *rdata)
+{
+ pjsip_msg *msg = rdata->msg_info.msg;
+ char info[80];
+
+ if (msg->type == PJSIP_REQUEST_MSG)
+ pj_ansi_snprintf(info, sizeof(info), "%.*s",
+ (int)msg->line.req.method.name.slen,
+ msg->line.req.method.name.ptr);
+ else
+ pj_ansi_snprintf(info, sizeof(info), "%d/%.*s",
+ msg->line.status.code,
+ (int)rdata->msg_info.cseq->method.name.slen,
+ rdata->msg_info.cseq->method.name.ptr);
+
+ TRACE_((THIS_FILE, " Received %s %s sdp", info,
+ (msg->body ? "with" : "without")));
+
+ return PJ_FALSE;
+}
+
+
+/* Message logger module. */
+static pjsip_module mod_msg_logger =
+{
+ NULL, NULL, /* prev and next */
+ { "mod-msg-loggee", 14}, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &log_on_rx_msg, /* on_rx_request() */
+ &log_on_rx_msg, /* on_rx_response() */
+ NULL, /* on_tx_request() */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+};
+
+static inv_test_param_t test_params[] =
+{
+/* Normal scenario:
+
+ UAC UAS
+ INVITE (offer) -->
+ 200/INVITE (answer) <--
+ ACK -->
+ */
+#if 0
+ {
+ "Standard INVITE with offer",
+ 0,
+ PJ_TRUE,
+ 1,
+ { OFFERER_UAC }
+ },
+
+ {
+ "Standard INVITE with offer, with 100rel",
+ PJSIP_INV_REQUIRE_100REL,
+ PJ_TRUE,
+ 1,
+ { OFFERER_UAC }
+ },
+#endif
+
+/* Delayed offer:
+ UAC UAS
+ INVITE (no SDP) -->
+ 200/INVITE (offer) <--
+ ACK (answer) -->
+ */
+#if 1
+ {
+ "INVITE with no offer",
+ 0,
+ PJ_TRUE,
+ 1,
+ { OFFERER_UAS }
+ },
+
+ {
+ "INVITE with no offer, with 100rel",
+ PJSIP_INV_REQUIRE_100REL,
+ PJ_TRUE,
+ 1,
+ { OFFERER_UAS }
+ },
+#endif
+
+/* Subsequent UAC offer with UPDATE:
+
+ UAC UAS
+ INVITE (offer) -->
+ 180/rel (answer) <--
+ UPDATE (offer) --> inv_update() on_rx_offer()
+ set_sdp_answer()
+ 200/UPDATE (answer) <--
+ 200/INVITE <--
+ ACK -->
+*/
+#if 1
+ {
+ "INVITE and UPDATE by UAC",
+ 0,
+ PJ_TRUE,
+ 2,
+ { OFFERER_UAC, OFFERER_UAC }
+ },
+ {
+ "INVITE and UPDATE by UAC, with 100rel",
+ PJSIP_INV_REQUIRE_100REL,
+ PJ_TRUE,
+ 2,
+ { OFFERER_UAC, OFFERER_UAC }
+ },
+#endif
+
+/* Subsequent UAS offer with UPDATE:
+
+ INVITE (offer -->
+ 180/rel (answer) <--
+ UPDATE (offer) <-- inv_update()
+ on_rx_offer()
+ set_sdp_answer()
+ 200/UPDATE (answer) -->
+ UPDATE (offer) --> on_rx_offer()
+ set_sdp_answer()
+ 200/UPDATE (answer) <--
+ 200/INVITE <--
+ ACK -->
+
+ */
+ {
+ "INVITE and many UPDATE by UAC and UAS",
+ 0,
+ PJ_TRUE,
+ 4,
+ { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS }
+ },
+
+};
+
+
+static pjsip_dialog* on_dlg_forked(pjsip_dialog *first_set, pjsip_rx_data *res)
+{
+ return NULL;
+}
+
+
+static void on_new_session(pjsip_inv_session *inv, pjsip_event *e)
+{
+}
+
+
+int inv_offer_answer_test(void)
+{
+ unsigned i;
+ int rc = 0;
+
+ /* Init UA layer */
+ if (pjsip_ua_instance()->id == -1) {
+ pjsip_ua_init_param ua_param;
+ pj_bzero(&ua_param, sizeof(ua_param));
+ ua_param.on_dlg_forked = &on_dlg_forked;
+ pjsip_ua_init_module(endpt, &ua_param);
+ }
+
+ /* Init inv-usage */
+ if (pjsip_inv_usage_instance()->id == -1) {
+ pjsip_inv_callback inv_cb;
+ pj_bzero(&inv_cb, sizeof(inv_cb));
+ inv_cb.on_media_update = &on_media_update;
+ inv_cb.on_rx_offer = &on_rx_offer;
+ inv_cb.on_create_offer = &on_create_offer;
+ inv_cb.on_state_changed = &on_state_changed;
+ inv_cb.on_new_session = &on_new_session;
+ pjsip_inv_usage_init(endpt, &inv_cb);
+ }
+
+ /* 100rel module */
+ pjsip_100rel_init_module(endpt);
+
+ /* Our module */
+ pjsip_endpt_register_module(endpt, &mod_inv_oa_test);
+ pjsip_endpt_register_module(endpt, &mod_msg_logger);
+
+ /* Create SIP UDP transport */
+ {
+ pj_sockaddr_in addr;
+ pjsip_transport *tp;
+ pj_status_t status;
+
+ pj_sockaddr_in_init(&addr, NULL, PORT);
+ status = pjsip_udp_transport_start(endpt, &addr, NULL, 1, &tp);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ /* Do tests */
+ for (i=0; i<PJ_ARRAY_SIZE(test_params); ++i) {
+ rc = perform_test(&test_params[i]);
+ if (rc != 0)
+ goto on_return;
+ }
+
+
+on_return:
+ return rc;
+}
+