* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/d5/d5a8e950de4299de84f003335e270da7ad3c90c8.svn-base b/jni/pjproject-android/.svn/pristine/d5/d5a8e950de4299de84f003335e270da7ad3c90c8.svn-base
new file mode 100644
index 0000000..3c43f53
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/d5/d5a8e950de4299de84f003335e270da7ad3c90c8.svn-base
@@ -0,0 +1,1658 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 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.h>
+#include <pjlib.h>
+
+#define THIS_FILE   "tsx_uas_test.c"
+
+
+/*****************************************************************************
+ **
+ ** UAS tests.
+ **
+ ** This file performs various tests for UAC transactions. Each test will have
+ ** a different Via branch param so that message receiver module and 
+ ** transaction user module can identify which test is being carried out.
+ **
+ ** TEST1_BRANCH_ID
+ **	Test that non-INVITE transaction returns 2xx response to the correct
+ **	transport and correctly terminates the transaction.
+ **	This also checks that transaction is destroyed immediately after
+ **	it sends final response when reliable transport is used.
+ **
+ ** TEST2_BRANCH_ID
+ **	As above, for non-2xx final response.
+ **
+ ** TEST3_BRANCH_ID
+ **	Transaction correctly progressing to PROCEEDING state when provisional
+ **	response is sent.
+ **
+ ** TEST4_BRANCH_ID
+ **	Transaction retransmits last response (if any) without notifying 
+ **	transaction user upon receiving request  retransmissions on TRYING
+ **	state
+ **
+ ** TEST5_BRANCH_ID
+ **	As above, in PROCEEDING state.
+ **
+ ** TEST6_BRANCH_ID
+ **	As above, in COMPLETED state, with first sending provisional response.
+ **	(Only applicable for non-reliable transports).
+ **
+ ** TEST7_BRANCH_ID
+ **	INVITE transaction MUST retransmit non-2xx final response.
+ **
+ ** TEST8_BRANCH_ID
+ **	As above, for INVITE's 2xx final response (this is PJSIP specific).
+ **
+ ** TEST9_BRANCH_ID
+ **	INVITE transaction MUST cease retransmission of final response when
+ **	ACK is received. (Note: PJSIP also retransmit 2xx final response 
+ **	until it's terminated by user).
+ **     Transaction also MUST terminate in T4 seconds.
+ **	(Only applicable for non-reliable transports).
+ **
+ ** TEST11_BRANCH_ID
+ **	Test scenario where transport fails before response is sent (i.e.
+ **	in TRYING state).
+ **
+ ** TEST12_BRANCH_ID
+ **	As above, after provisional response is sent but before final
+ **	response is sent (i.e. in PROCEEDING state).
+ **
+ ** TEST13_BRANCH_ID
+ **	As above, for INVITE, after final response has been sent but before
+ **	ACK is received (i.e. in CONNECTED state).
+ **
+ ** TEST14_BRANCH_ID
+ **	When UAS failed to deliver the response with the selected transport,
+ **	it should try contacting the client with other transport or begin
+ **	RFC 3263 server resolution procedure.
+ **	This should be tested on:
+ **	    a. TRYING state (when delivering first response).
+ **	    b. PROCEEDING state (when failed to retransmit last response
+ **	       upon receiving request retransmission).
+ **	    c. COMPLETED state.
+ **
+ **/
+
+#define TEST1_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test1")
+#define TEST2_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test2")
+#define TEST3_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test3")
+#define TEST4_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test4")
+#define TEST5_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test5")
+#define TEST6_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test6")
+#define TEST7_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test7")
+#define TEST8_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test8")
+#define TEST9_BRANCH_ID  (PJSIP_RFC3261_BRANCH_ID "-UAS-Test9")
+#define TEST10_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test10")
+#define TEST11_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test11")
+#define TEST12_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test12")
+//#define TEST13_BRANCH_ID (PJSIP_RFC3261_BRANCH_ID "-UAS-Test13")
+
+#define TEST1_STATUS_CODE	200
+#define TEST2_STATUS_CODE	301
+#define TEST3_PROVISIONAL_CODE	PJSIP_SC_QUEUED
+#define TEST3_STATUS_CODE	202
+#define TEST4_STATUS_CODE	200
+#define TEST4_REQUEST_COUNT	2
+#define TEST5_PROVISIONAL_CODE	100
+#define TEST5_STATUS_CODE	200	
+#define TEST5_REQUEST_COUNT	2
+#define TEST5_RESPONSE_COUNT	2
+#define TEST6_PROVISIONAL_CODE	100
+#define TEST6_STATUS_CODE	200	/* Must be final */
+#define TEST6_REQUEST_COUNT	2
+#define TEST6_RESPONSE_COUNT	3
+#define TEST7_STATUS_CODE	301
+#define TEST8_STATUS_CODE	302
+#define TEST9_STATUS_CODE	301
+
+
+#define TEST4_TITLE "test4: absorbing request retransmission"
+#define TEST5_TITLE "test5: retransmit last response in PROCEEDING state"
+#define TEST6_TITLE "test6: retransmit last response in COMPLETED state"
+
+
+static char TARGET_URI[128];
+static char FROM_URI[128];
+static struct tsx_test_param *test_param;
+static unsigned tp_flag;
+
+
+#define TEST_TIMEOUT_ERROR	-30
+#define MAX_ALLOWED_DIFF	150
+
+static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e);
+static pj_bool_t on_rx_message(pjsip_rx_data *rdata);
+
+/* UAC transaction user module. */
+static pjsip_module tsx_user = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Tsx-UAS-User", 12},		/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_APPLICATION-1,	/* Priority		*/
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    NULL,				/* on_rx_request()	*/
+    NULL,				/* on_rx_response()	*/
+    NULL,				/* on_tx_request()	*/
+    NULL,				/* on_tx_response()	*/
+    &tsx_user_on_tsx_state,		/* on_tsx_state()	*/
+};
+
+/* Module to send request. */
+static pjsip_module msg_sender = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Msg-Sender", 10},		/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_APPLICATION-1,	/* Priority		*/
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    &on_rx_message,			/* on_rx_request()	*/
+    &on_rx_message,			/* on_rx_response()	*/
+    NULL,				/* on_tx_request()	*/
+    NULL,				/* on_tx_response()	*/
+    NULL,				/* on_tsx_state()	*/
+};
+
+/* Static vars, which will be reset on each test. */
+static int recv_count;
+static pj_time_val recv_last;
+static pj_bool_t test_complete;
+
+/* Loop transport instance. */
+static pjsip_transport *loop;
+
+/* UAS transaction key. */
+static char key_buf[64];
+static pj_str_t tsx_key = { key_buf, 0 };
+
+
+/* General timer entry to be used by tests. */
+//static pj_timer_entry timer;
+
+/* Timer to send response via transaction. */
+struct response
+{
+    pj_str_t	     tsx_key;
+    pjsip_tx_data   *tdata;
+};
+
+/* Timer callback to send response. */
+static void send_response_timer( pj_timer_heap_t *timer_heap,
+				 struct pj_timer_entry *entry)
+{
+    pjsip_transaction *tsx;
+    struct response *r = (struct response*) entry->user_data;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(timer_heap);
+
+    tsx = pjsip_tsx_layer_find_tsx(&r->tsx_key, PJ_TRUE);
+    if (!tsx) {
+	PJ_LOG(3,(THIS_FILE,"    error: timer unable to find transaction"));
+	pjsip_tx_data_dec_ref(r->tdata);
+	return;
+    }
+
+    status = pjsip_tsx_send_msg(tsx, r->tdata);
+    if (status != PJ_SUCCESS) {
+	// Some tests do expect failure!
+	//PJ_LOG(3,(THIS_FILE,"    error: timer unable to send response"));
+	pj_grp_lock_release(tsx->grp_lock);
+	pjsip_tx_data_dec_ref(r->tdata);
+	return;
+    }
+
+    pj_grp_lock_release(tsx->grp_lock);
+}
+
+/* Utility to send response. */
+static void send_response( pjsip_rx_data *rdata,
+			   pjsip_transaction *tsx,
+			   int status_code )
+{
+    pj_status_t status;
+    pjsip_tx_data *tdata;
+
+    status = pjsip_endpt_create_response( endpt, rdata, status_code, NULL, 
+					  &tdata);
+    if (status != PJ_SUCCESS) {
+	app_perror("    error: unable to create response", status);
+	test_complete = -196;
+	return;
+    }
+
+    status = pjsip_tsx_send_msg(tsx, tdata);
+    if (status != PJ_SUCCESS) {
+	pjsip_tx_data_dec_ref(tdata);
+	// Some tests do expect failure!
+	//app_perror("    error: unable to send response", status);
+	//test_complete = -197;
+	return;
+    }
+}
+
+/* Schedule timer to send response for the specified UAS transaction */
+static void schedule_send_response( pjsip_rx_data *rdata,
+				    const pj_str_t *tsx_key,
+				    int status_code,
+				    int msec_delay )
+{
+    pj_status_t status;
+    pjsip_tx_data *tdata;
+    pj_timer_entry *t;
+    struct response *r;
+    pj_time_val delay;
+
+    status = pjsip_endpt_create_response( endpt, rdata, status_code, NULL, 
+					  &tdata);
+    if (status != PJ_SUCCESS) {
+	app_perror("    error: unable to create response", status);
+	test_complete = -198;
+	return;
+    }
+
+    r = PJ_POOL_ALLOC_T(tdata->pool, struct response);
+    pj_strdup(tdata->pool, &r->tsx_key, tsx_key);
+    r->tdata = tdata;
+
+    delay.sec = 0;
+    delay.msec = msec_delay;
+    pj_time_val_normalize(&delay);
+
+    t = PJ_POOL_ZALLOC_T(tdata->pool, pj_timer_entry);
+    t->user_data = r;
+    t->cb = &send_response_timer;
+
+    status = pjsip_endpt_schedule_timer(endpt, t, &delay);
+    if (status != PJ_SUCCESS) {
+	pjsip_tx_data_dec_ref(tdata);
+	app_perror("    error: unable to schedule timer", status);
+	test_complete = -199;
+	return;
+    }
+}
+
+
+/* Find and terminate tsx with the specified key. */
+static void terminate_our_tsx(int status_code)
+{
+    pjsip_transaction *tsx;
+
+    tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
+    if (!tsx) {
+	PJ_LOG(3,(THIS_FILE,"    error: timer unable to find transaction"));
+	return;
+    }
+
+    pjsip_tsx_terminate(tsx, status_code);
+    pj_grp_lock_release(tsx->grp_lock);
+}
+
+#if 0	/* Unused for now */
+/* Timer callback to terminate transaction. */
+static void terminate_tsx_timer( pj_timer_heap_t *timer_heap,
+				 struct pj_timer_entry *entry)
+{
+    terminate_our_tsx(entry->id);
+}
+
+
+/* Schedule timer to terminate transaction. */
+static void schedule_terminate_tsx( pjsip_transaction *tsx,
+				    int status_code,
+				    int msec_delay )
+{
+    pj_time_val delay;
+
+    delay.sec = 0;
+    delay.msec = msec_delay;
+    pj_time_val_normalize(&delay);
+
+    pj_assert(pj_strcmp(&tsx->transaction_key, &tsx_key)==0);
+    timer.user_data = NULL;
+    timer.id = status_code;
+    timer.cb = &terminate_tsx_timer;
+    pjsip_endpt_schedule_timer(endpt, &timer, &delay);
+}
+#endif
+
+
+/*
+ * This is the handler to receive state changed notification from the
+ * transaction. It is used to verify that the transaction behaves according
+ * to the test scenario.
+ */
+static void tsx_user_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
+{
+    if (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0 ||
+	pj_stricmp2(&tsx->branch, TEST2_BRANCH_ID)==0) 
+    {
+	/*
+	 * TEST1_BRANCH_ID tests that non-INVITE transaction transmits final
+	 * response using correct transport and terminates transaction after
+	 * T4 (PJSIP_T4_TIMEOUT, 5 seconds).
+	 *
+	 * TEST2_BRANCH_ID does similar test for non-2xx final response.
+	 */
+	int status_code = (pj_stricmp2(&tsx->branch, TEST1_BRANCH_ID)==0) ?
+			  TEST1_STATUS_CODE : TEST2_STATUS_CODE;
+
+	if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	    test_complete = 1;
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != status_code) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -100;
+	    }
+	    
+	    /* Previous state must be completed. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -101;
+	    }
+
+	} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+
+	    /* Previous state must be TRYING. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -102;
+	    }
+	}
+
+    }
+    else
+    if (pj_stricmp2(&tsx->branch, TEST3_BRANCH_ID)==0) {
+	/*
+	 * TEST3_BRANCH_ID tests sending provisional response.
+	 */
+	if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	    test_complete = 1;
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST3_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -110;
+	    }
+	    
+	    /* Previous state must be completed. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -111;
+	    }
+
+	} else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) {
+
+	    /* Previous state must be TRYING. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -112;
+	    }
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST3_PROVISIONAL_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -113;
+	    }
+
+	    /* Check that event must be TX_MSG */
+	    if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect event"));
+		test_complete = -114;
+	    }
+
+	} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+
+	    /* Previous state must be PROCEEDING. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -115;
+	    }
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST3_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -116;
+	    }
+
+	    /* Check that event must be TX_MSG */
+	    if (e->body.tsx_state.type != PJSIP_EVENT_TX_MSG) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect event"));
+		test_complete = -117;
+	    }
+
+	}
+
+    } else
+    if (pj_stricmp2(&tsx->branch, TEST4_BRANCH_ID)==0) {
+	/*
+	 * TEST4_BRANCH_ID tests receiving retransmissions in TRYING state.
+	 */
+	if (tsx->state == PJSIP_TSX_STATE_TRYING) {
+	    /* Request is received. */
+	} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST4_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, 
+			  "    error: incorrect status code %d "
+			  "(expecting %d)", tsx->status_code,
+			  TEST4_STATUS_CODE));
+		test_complete = -120;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -121;
+	    }
+
+	} else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) 
+	{
+	    PJ_LOG(3,(THIS_FILE, "    error: unexpected state %s (122)",
+		      pjsip_tsx_state_str(tsx->state)));
+	    test_complete = -122;
+
+	}
+
+
+    } else
+    if (pj_stricmp2(&tsx->branch, TEST5_BRANCH_ID)==0) {
+	/*
+	 * TEST5_BRANCH_ID tests receiving retransmissions in PROCEEDING state
+	 */
+	if (tsx->state == PJSIP_TSX_STATE_TRYING) {
+	    /* Request is received. */
+
+	} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST5_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -130;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_PROCEEDING) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -131;
+	    }
+
+	} else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) {
+
+	    /* Check status code. */
+	    if (tsx->status_code != TEST5_PROVISIONAL_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -132;
+	    }
+
+	} else if (tsx->state != PJSIP_TSX_STATE_DESTROYED) {
+	    PJ_LOG(3,(THIS_FILE, "    error: unexpected state %s (133)",
+		      pjsip_tsx_state_str(tsx->state)));
+	    test_complete = -133;
+
+	}
+
+    } else
+    if (pj_stricmp2(&tsx->branch, TEST6_BRANCH_ID)==0) {
+	/*
+	 * TEST6_BRANCH_ID tests receiving retransmissions in COMPLETED state
+	 */
+	if (tsx->state == PJSIP_TSX_STATE_TRYING) {
+	    /* Request is received. */
+
+	} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST6_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code %d "
+			  "(expecting %d)", tsx->status_code, 
+			  TEST6_STATUS_CODE));
+		test_complete = -140;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -141;
+	    }
+
+	} else if (tsx->state != PJSIP_TSX_STATE_PROCEEDING &&
+		   tsx->state != PJSIP_TSX_STATE_COMPLETED &&
+		   tsx->state != PJSIP_TSX_STATE_DESTROYED) 
+	{
+	    PJ_LOG(3,(THIS_FILE, "    error: unexpected state %s (142)",
+		      pjsip_tsx_state_str(tsx->state)));
+	    test_complete = -142;
+
+	}
+
+
+    } else
+    if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID)==0 ||
+	pj_stricmp2(&tsx->branch, TEST8_BRANCH_ID)==0) 
+    {
+	/*
+	 * TEST7_BRANCH_ID and TEST8_BRANCH_ID test retransmission of
+	 * INVITE final response
+	 */
+	int code;
+
+	if (pj_stricmp2(&tsx->branch, TEST7_BRANCH_ID) == 0)
+	    code = TEST7_STATUS_CODE;
+	else
+	    code = TEST8_STATUS_CODE;
+
+	if (tsx->state == PJSIP_TSX_STATE_TRYING) {
+	    /* Request is received. */
+
+	} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	    if (test_complete == 0)
+		test_complete = 1;
+
+	    /* Check status code. */
+	    if (tsx->status_code != PJSIP_SC_TSX_TIMEOUT) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -150;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -151;
+	    }
+
+	    /* Check the number of retransmissions */
+	    if (tp_flag & PJSIP_TRANSPORT_RELIABLE) {
+
+		if (tsx->retransmit_count != 0) {
+		    PJ_LOG(3,(THIS_FILE, "    error: should not retransmit"));
+		    test_complete = -1510;
+		}
+
+	    } else {
+
+		if (tsx->retransmit_count != 10) {
+		    PJ_LOG(3,(THIS_FILE, 
+			      "    error: incorrect retransmit count %d "
+			      "(expecting 10)",
+			      tsx->retransmit_count));
+		    test_complete = -1510;
+		}
+
+	    }
+
+	} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != code) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -152;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -153;
+	    }
+
+	} else if (tsx->state != PJSIP_TSX_STATE_DESTROYED)  {
+
+	    PJ_LOG(3,(THIS_FILE, "    error: unexpected state (154)"));
+	    test_complete = -154;
+
+	}
+
+
+    } else
+    if (pj_stricmp2(&tsx->branch, TEST9_BRANCH_ID)==0)  {
+	/*
+	 * TEST9_BRANCH_ID tests that retransmission of INVITE final response
+	 * must cease when ACK is received.
+	 */
+
+	if (tsx->state == PJSIP_TSX_STATE_TRYING) {
+	    /* Request is received. */
+
+	} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+	    if (test_complete == 0)
+		test_complete = 1;
+
+	    /* Check status code. */
+	    if (tsx->status_code != TEST9_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -160;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_CONFIRMED) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -161;
+	    }
+
+	} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST9_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -162;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_TRYING) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -163;
+	    }
+
+
+	} else if (tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
+
+	    /* Check that status code is status_code. */
+	    if (tsx->status_code != TEST9_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -164;
+	    }
+	    
+	    /* Previous state. */
+	    if (e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect prev_state"));
+		test_complete = -165;
+	    }
+
+	} else if (tsx->state != PJSIP_TSX_STATE_DESTROYED)  {
+
+	    PJ_LOG(3,(THIS_FILE, "    error: unexpected state (166)"));
+	    test_complete = -166;
+
+	}
+
+
+    } else
+    if (pj_stricmp2(&tsx->branch, TEST10_BRANCH_ID)==0 ||
+	pj_stricmp2(&tsx->branch, TEST11_BRANCH_ID)==0 ||
+	pj_stricmp2(&tsx->branch, TEST12_BRANCH_ID)==0)  
+    {
+	if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+	    
+	    if (!test_complete)
+		test_complete = 1;
+
+	    if (tsx->status_code != PJSIP_SC_TSX_TRANSPORT_ERROR) {
+		PJ_LOG(3,(THIS_FILE,"    error: incorrect status code"));
+		test_complete = -170;
+	    }
+	}
+    }
+
+}
+
+/* Save transaction key to global variables. */
+static void save_key(pjsip_transaction *tsx)
+{
+    pj_str_t key;
+
+    pj_strdup(tsx->pool, &key, &tsx->transaction_key);
+    pj_strcpy(&tsx_key, &key);
+}
+
+#define DIFF(a,b)   ((a<b) ? (b-a) : (a-b))
+
+/*
+ * Message receiver handler.
+ */
+static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
+{
+    pjsip_msg *msg = rdata->msg_info.msg;
+    pj_str_t branch_param = rdata->msg_info.via->branch_param;
+    pj_status_t status;
+
+    if (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0 ||
+	pj_stricmp2(&branch_param, TEST2_BRANCH_ID) == 0) 
+    {
+	/*
+	 * TEST1_BRANCH_ID tests that non-INVITE transaction transmits 2xx 
+	 * final response using correct transport and terminates transaction 
+	 * after 32 seconds.
+	 *
+	 * TEST2_BRANCH_ID performs similar test for non-2xx final response.
+	 */
+	int status_code = (pj_stricmp2(&branch_param, TEST1_BRANCH_ID) == 0) ?
+			  TEST1_STATUS_CODE : TEST2_STATUS_CODE;
+
+	if (msg->type == PJSIP_REQUEST_MSG) {
+	    /* On received request, create UAS and respond with final 
+	     * response. 
+	     */
+	    pjsip_transaction *tsx;
+
+	    status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
+	    if (status != PJ_SUCCESS) {
+		app_perror("    error: unable to create transaction", status);
+		test_complete = -110;
+		return PJ_TRUE;
+	    }
+	    pjsip_tsx_recv_msg(tsx, rdata);
+
+	    save_key(tsx);
+	    send_response(rdata, tsx, status_code);
+
+	} else {
+	    /* Verify the response received. */
+
+	    ++recv_count;
+
+	    /* Verify status code. */
+	    if (msg->line.status.code != status_code) {
+		PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		test_complete = -113;
+	    }
+
+	    /* Verify that no retransmissions is received. */
+	    if (recv_count > 1) {
+		PJ_LOG(3,(THIS_FILE, "    error: retransmission received"));
+		test_complete = -114;
+	    }
+
+	}
+	return PJ_TRUE;
+
+    } else if (pj_stricmp2(&branch_param, TEST3_BRANCH_ID) == 0) {
+
+	/* TEST3_BRANCH_ID tests provisional response. */
+
+	if (msg->type == PJSIP_REQUEST_MSG) {
+	    /* On received request, create UAS and respond with provisional
+	     * response, then schedule timer to send final response.
+	     */
+	    pjsip_transaction *tsx;
+
+	    status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
+	    if (status != PJ_SUCCESS) {
+		app_perror("    error: unable to create transaction", status);
+		test_complete = -116;
+		return PJ_TRUE;
+	    }
+	    pjsip_tsx_recv_msg(tsx, rdata);
+
+	    save_key(tsx);
+
+	    send_response(rdata, tsx, TEST3_PROVISIONAL_CODE);
+	    schedule_send_response(rdata, &tsx->transaction_key, 
+				   TEST3_STATUS_CODE, 2000);
+
+	} else {
+	    /* Verify the response received. */
+
+	    ++recv_count;
+
+	    if (recv_count == 1) {
+		/* Verify status code. */
+		if (msg->line.status.code != TEST3_PROVISIONAL_CODE) {
+		    PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		    test_complete = -123;
+		}
+	    } else if (recv_count == 2) {
+		/* Verify status code. */
+		if (msg->line.status.code != TEST3_STATUS_CODE) {
+		    PJ_LOG(3,(THIS_FILE, "    error: incorrect status code"));
+		    test_complete = -124;
+		}
+	    } else {
+		PJ_LOG(3,(THIS_FILE, "    error: retransmission received"));
+		test_complete = -125;
+	    }
+
+	}
+	return PJ_TRUE;
+
+    } else if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0 ||
+	       pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0 ||
+	       pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) 
+    {
+
+	/* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state. */
+	/* TEST5_BRANCH_ID: retransmit last response in PROCEEDING state. */
+	/* TEST6_BRANCH_ID: retransmit last response in COMPLETED state. */
+
+	if (msg->type == PJSIP_REQUEST_MSG) {
+	    /* On received request, create UAS. */
+	    pjsip_transaction *tsx;
+
+	    PJ_LOG(4,(THIS_FILE, "    received request (probably retransmission)"));
+
+	    status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
+	    if (status != PJ_SUCCESS) {
+		app_perror("    error: unable to create transaction", status);
+		test_complete = -130;
+		return PJ_TRUE;
+	    }
+
+	    pjsip_tsx_recv_msg(tsx, rdata);
+	    save_key(tsx);
+
+	    if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
+
+	    } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) {
+		send_response(rdata, tsx, TEST5_PROVISIONAL_CODE);
+
+	    } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) {
+		PJ_LOG(4,(THIS_FILE, "    sending provisional response"));
+		send_response(rdata, tsx, TEST6_PROVISIONAL_CODE);
+		PJ_LOG(4,(THIS_FILE, "    sending final response"));
+		send_response(rdata, tsx, TEST6_STATUS_CODE);
+	    }
+
+	} else {
+	    /* Verify the response received. */
+	    
+	    PJ_LOG(4,(THIS_FILE, "    received response number %d", recv_count));
+
+	    ++recv_count;
+
+	    if (pj_stricmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
+		PJ_LOG(3,(THIS_FILE, "    error: not expecting response!"));
+		test_complete = -132;
+
+	    } else if (pj_stricmp2(&branch_param, TEST5_BRANCH_ID) == 0) {
+
+		if (rdata->msg_info.msg->line.status.code!=TEST5_PROVISIONAL_CODE) {
+		    PJ_LOG(3,(THIS_FILE, "    error: incorrect status code!"));
+		    test_complete = -133;
+
+		} 
+		if (recv_count > TEST5_RESPONSE_COUNT) {
+		    PJ_LOG(3,(THIS_FILE, "    error: not expecting response!"));
+		    test_complete = -134;
+		}
+
+	    } else if (pj_stricmp2(&branch_param, TEST6_BRANCH_ID) == 0) {
+
+		int code = rdata->msg_info.msg->line.status.code;
+
+		switch (recv_count) {
+		case 1:
+		    if (code != TEST6_PROVISIONAL_CODE) {
+			PJ_LOG(3,(THIS_FILE, "    error: invalid code!"));
+			test_complete = -135;
+		    }
+		    break;
+		case 2:
+		case 3:
+		    if (code != TEST6_STATUS_CODE) {
+			PJ_LOG(3,(THIS_FILE, "    error: invalid code %d "
+				  "(expecting %d)", code, TEST6_STATUS_CODE));
+			test_complete = -136;
+		    }
+		    break;
+		default:
+		    PJ_LOG(3,(THIS_FILE, "    error: not expecting response"));
+		    test_complete = -137;
+		    break;
+		}
+	    }
+	}
+	return PJ_TRUE;
+
+
+    } else if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0 ||
+	       pj_stricmp2(&branch_param, TEST8_BRANCH_ID) == 0) 
+    {
+
+	/*
+	 * TEST7_BRANCH_ID and TEST8_BRANCH_ID test the retransmission
+	 * of INVITE final response
+	 */
+	if (msg->type == PJSIP_REQUEST_MSG) {
+
+	    /* On received request, create UAS. */
+	    pjsip_transaction *tsx;
+
+	    status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
+	    if (status != PJ_SUCCESS) {
+		app_perror("    error: unable to create transaction", status);
+		test_complete = -140;
+		return PJ_TRUE;
+	    }
+
+	    pjsip_tsx_recv_msg(tsx, rdata);
+	    save_key(tsx);
+
+	    if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0) {
+
+		send_response(rdata, tsx, TEST7_STATUS_CODE);
+
+	    } else {
+
+		send_response(rdata, tsx, TEST8_STATUS_CODE);
+
+	    }
+
+	} else {
+	    int code;
+
+	    ++recv_count;
+
+	    if (pj_stricmp2(&branch_param, TEST7_BRANCH_ID) == 0)
+		code = TEST7_STATUS_CODE;
+	    else
+		code = TEST8_STATUS_CODE;
+
+	    if (recv_count==1) {
+		
+		if (rdata->msg_info.msg->line.status.code != code) {
+		    PJ_LOG(3,(THIS_FILE,"    error: invalid status code"));
+		    test_complete = -141;
+		}
+
+		recv_last = rdata->pkt_info.timestamp;
+
+	    } else {
+
+		pj_time_val now;
+		unsigned msec, msec_expected;
+
+		now = rdata->pkt_info.timestamp;
+
+		PJ_TIME_VAL_SUB(now, recv_last);
+	    
+		msec = now.sec*1000 + now.msec;
+		msec_expected = (1 << (recv_count-2)) * pjsip_cfg()->tsx.t1;
+		if (msec_expected > pjsip_cfg()->tsx.t2)
+		    msec_expected = pjsip_cfg()->tsx.t2;
+
+		if (DIFF(msec, msec_expected) > MAX_ALLOWED_DIFF) {
+		    PJ_LOG(3,(THIS_FILE,
+			      "    error: incorrect retransmission "
+			      "time (%d ms expected, %d ms received",
+			      msec_expected, msec));
+		    test_complete = -142;
+		}
+
+		if (recv_count > 11) {
+		    PJ_LOG(3,(THIS_FILE,"    error: too many responses (%d)",
+					recv_count));
+		    test_complete = -143;
+		}
+
+		recv_last = rdata->pkt_info.timestamp;
+	    }
+
+	}
+	return PJ_TRUE;
+
+    } else if (pj_stricmp2(&branch_param, TEST9_BRANCH_ID) == 0) {
+
+	/*
+	 * TEST9_BRANCH_ID tests that the retransmission of INVITE final 
+	 * response should cease when ACK is received. Transaction also MUST
+	 * terminate in T4 seconds.
+	 */
+	if (msg->type == PJSIP_REQUEST_MSG) {
+
+	    /* On received request, create UAS. */
+	    pjsip_transaction *tsx;
+
+	    status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
+	    if (status != PJ_SUCCESS) {
+		app_perror("    error: unable to create transaction", status);
+		test_complete = -150;
+		return PJ_TRUE;
+	    }
+
+	    pjsip_tsx_recv_msg(tsx, rdata);
+	    save_key(tsx);
+	    send_response(rdata, tsx, TEST9_STATUS_CODE);
+
+
+	} else {
+
+	    ++recv_count;
+
+	    if (rdata->msg_info.msg->line.status.code != TEST9_STATUS_CODE) {
+		PJ_LOG(3,(THIS_FILE,"    error: invalid status code"));
+		test_complete = -151;
+	    }
+
+	    if (recv_count==1) {
+
+		recv_last = rdata->pkt_info.timestamp;
+
+	    } else if (recv_count < 5) {
+
+		/* Let UAS retransmit some messages before we send ACK. */
+		pj_time_val now;
+		unsigned msec, msec_expected;
+
+		now = rdata->pkt_info.timestamp;
+
+		PJ_TIME_VAL_SUB(now, recv_last);
+	    
+		msec = now.sec*1000 + now.msec;
+		msec_expected = (1 << (recv_count-2)) * pjsip_cfg()->tsx.t1;
+		if (msec_expected > pjsip_cfg()->tsx.t2)
+		    msec_expected = pjsip_cfg()->tsx.t2;
+
+		if (DIFF(msec, msec_expected) > MAX_ALLOWED_DIFF) {
+		    PJ_LOG(3,(THIS_FILE,
+			      "    error: incorrect retransmission "
+			      "time (%d ms expected, %d ms received",
+			      msec_expected, msec));
+		    test_complete = -152;
+		}
+
+		recv_last = rdata->pkt_info.timestamp;
+
+	    } else if (recv_count == 5) {
+		pjsip_tx_data *tdata;
+		pjsip_sip_uri *uri;
+		pjsip_via_hdr *via;
+
+		status = pjsip_endpt_create_request_from_hdr(
+			    endpt, &pjsip_ack_method, 
+			    rdata->msg_info.to->uri,
+			    rdata->msg_info.from,
+			    rdata->msg_info.to,
+			    NULL, 
+			    rdata->msg_info.cid,
+			    rdata->msg_info.cseq->cseq,
+			    NULL,
+			    &tdata);
+		if (status != PJ_SUCCESS) {
+		    app_perror("    error: unable to create ACK", status);
+		    test_complete = -153;
+		    return PJ_TRUE;
+		}
+
+		uri=(pjsip_sip_uri*)pjsip_uri_get_uri(tdata->msg->line.req.uri);
+		uri->transport_param = pj_str("loop-dgram");
+
+		via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+		via->branch_param = pj_str(TEST9_BRANCH_ID);
+
+		status = pjsip_endpt_send_request_stateless(endpt, tdata,
+							    NULL, NULL);
+		if (status != PJ_SUCCESS) {
+		    app_perror("    error: unable to send ACK", status);
+		    test_complete = -154;
+		}
+
+	    } else {
+		PJ_LOG(3,(THIS_FILE,"    error: too many responses (%d)",
+				    recv_count));
+		test_complete = -155;
+	    }
+
+	}
+	return PJ_TRUE;
+
+    } else if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0 ||
+	       pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0 ||
+	       pj_stricmp2(&branch_param, TEST12_BRANCH_ID) == 0) 
+    {
+	int test_num, code1, code2;
+
+	if (pj_stricmp2(&branch_param, TEST10_BRANCH_ID) == 0)
+	    test_num=10, code1 = 100, code2 = 0;
+	else if (pj_stricmp2(&branch_param, TEST11_BRANCH_ID) == 0)
+	    test_num=11, code1 = 100, code2 = 200;
+	else
+	    test_num=12, code1 = 200, code2 = 0;
+
+	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+
+	    /* On received response, create UAS. */
+	    pjsip_transaction *tsx;
+
+	    status = pjsip_tsx_create_uas(&tsx_user, rdata, &tsx);
+	    if (status != PJ_SUCCESS) {
+		app_perror("    error: unable to create transaction", status);
+		test_complete = -150;
+		return PJ_TRUE;
+	    }
+
+	    pjsip_tsx_recv_msg(tsx, rdata);
+	    save_key(tsx);
+	    
+	    schedule_send_response(rdata, &tsx_key, code1, 1000);
+
+	    if (code2)
+		schedule_send_response(rdata, &tsx_key, code2, 2000);
+
+	} else {
+
+	}
+
+	return PJ_TRUE;
+    }
+
+    return PJ_FALSE;
+}
+
+/* 
+ * The generic test framework, used by most of the tests. 
+ */
+static int perform_test( char *target_uri, char *from_uri, 
+			 char *branch_param, int test_time, 
+			 const pjsip_method *method,
+			 int request_cnt, int request_interval_msec,
+			 int expecting_timeout)
+{
+    pjsip_tx_data *tdata;
+    pj_str_t target, from;
+    pjsip_via_hdr *via;
+    pj_time_val timeout, next_send;
+    int sent_cnt;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, 
+	      "   please standby, this will take at most %d seconds..",
+	      test_time));
+
+    /* Reset test. */
+    recv_count = 0;
+    test_complete = 0;
+    tsx_key.slen = 0;
+
+    /* Init headers. */
+    target = pj_str(target_uri);
+    from = pj_str(from_uri);
+
+    /* Create request. */
+    status = pjsip_endpt_create_request( endpt, method, &target,
+					 &from, &target, NULL, NULL, -1, 
+					 NULL, &tdata);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to create request", status);
+	return -10;
+    }
+
+    /* Set the branch param for test 1. */
+    via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+    via->branch_param = pj_str(branch_param);
+
+    /* Schedule first send. */
+    sent_cnt = 0;
+    pj_gettimeofday(&next_send);
+    pj_time_val_normalize(&next_send);
+
+    /* Set test completion time. */
+    pj_gettimeofday(&timeout);
+    timeout.sec += test_time;
+
+    /* Wait until test complete. */
+    while (!test_complete) {
+	pj_time_val now, poll_delay = {0, 10};
+
+	pjsip_endpt_handle_events(endpt, &poll_delay);
+
+	pj_gettimeofday(&now);
+
+	if (sent_cnt < request_cnt && PJ_TIME_VAL_GTE(now, next_send)) {
+	    /* Add additional reference to tdata to prevent transaction from
+	     * deleting it.
+	     */
+	    pjsip_tx_data_add_ref(tdata);
+
+	    /* (Re)Send the request. */
+	    PJ_LOG(4,(THIS_FILE, "    (re)sending request %d", sent_cnt));
+
+	    status = pjsip_endpt_send_request_stateless(endpt, tdata, 0, 0);
+	    if (status != PJ_SUCCESS) {
+		app_perror("   Error: unable to send request", status);
+		pjsip_tx_data_dec_ref(tdata);
+		return -20;
+	    }
+
+	    /* Schedule next send, if any. */
+	    sent_cnt++;
+	    if (sent_cnt < request_cnt) {
+		pj_gettimeofday(&next_send);
+		next_send.msec += request_interval_msec;
+		pj_time_val_normalize(&next_send);
+	    }
+	}
+
+	if (now.sec > timeout.sec) {
+	    if (!expecting_timeout)
+		PJ_LOG(3,(THIS_FILE, "   Error: test has timed out"));
+	    pjsip_tx_data_dec_ref(tdata);
+	    return TEST_TIMEOUT_ERROR;
+	}
+    }
+
+    if (test_complete < 0) {
+	pjsip_transaction *tsx;
+
+	tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE);
+	if (tsx) {
+	    pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED);
+	    pj_grp_lock_release(tsx->grp_lock);
+	    flush_events(1000);
+	}
+	pjsip_tx_data_dec_ref(tdata);
+	return test_complete;
+    }
+
+    /* Allow transaction to destroy itself */
+    flush_events(500);
+
+    /* Make sure transaction has been destroyed. */
+    if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) {
+	PJ_LOG(3,(THIS_FILE, "   Error: transaction has not been destroyed"));
+	pjsip_tx_data_dec_ref(tdata);
+	return -40;
+    }
+
+    /* Check tdata reference counter. */
+    if (pj_atomic_get(tdata->ref_cnt) != 1) {
+	PJ_LOG(3,(THIS_FILE, "   Error: tdata reference counter is %d",
+		      pj_atomic_get(tdata->ref_cnt)));
+	pjsip_tx_data_dec_ref(tdata);
+	return -50;
+    }
+
+    /* Destroy txdata */
+    pjsip_tx_data_dec_ref(tdata);
+
+    return PJ_SUCCESS;
+
+}
+
+
+/*****************************************************************************
+ **
+ ** TEST1_BRANCH_ID: Basic 2xx final response
+ ** TEST2_BRANCH_ID: Basic non-2xx final response
+ **
+ *****************************************************************************
+ */
+static int tsx_basic_final_response_test(void)
+{
+    unsigned duration;
+    int status;
+
+    PJ_LOG(3,(THIS_FILE,"  test1: basic sending 2xx final response"));
+
+    /* Test duration must be greater than 32 secs if unreliable transport
+     * is used.
+     */
+    duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33;
+
+    status = perform_test(TARGET_URI, FROM_URI, TEST1_BRANCH_ID,
+			  duration,  &pjsip_options_method, 1, 0, 0);
+    if (status != 0)
+	return status;
+
+    PJ_LOG(3,(THIS_FILE,"  test2: basic sending non-2xx final response"));
+
+    status = perform_test(TARGET_URI, FROM_URI, TEST2_BRANCH_ID,
+			  duration, &pjsip_options_method, 1, 0, 0);
+    if (status != 0)
+	return status;
+
+    return 0;
+}
+
+
+/*****************************************************************************
+ **
+ ** TEST3_BRANCH_ID: Sending provisional response
+ **
+ *****************************************************************************
+ */
+static int tsx_basic_provisional_response_test(void)
+{
+    unsigned duration;
+    int status;
+
+    PJ_LOG(3,(THIS_FILE,"  test3: basic sending 2xx final response"));
+
+    duration = (tp_flag & PJSIP_TRANSPORT_RELIABLE) ? 1 : 33;
+    duration += 2;
+
+    status = perform_test(TARGET_URI, FROM_URI, TEST3_BRANCH_ID, duration,
+			  &pjsip_options_method, 1, 0, 0);
+
+    return status;
+}
+
+
+/*****************************************************************************
+ **
+ ** TEST4_BRANCH_ID: Absorbs retransmissions in TRYING state
+ ** TEST5_BRANCH_ID: Absorbs retransmissions in PROCEEDING state
+ ** TEST6_BRANCH_ID: Absorbs retransmissions in COMPLETED state
+ **
+ *****************************************************************************
+ */
+static int tsx_retransmit_last_response_test(const char *title,
+					     char *branch_id,
+					     int request_cnt,
+					     int status_code)
+{
+    int status;
+
+    PJ_LOG(3,(THIS_FILE,"  %s", title));
+
+    status = perform_test(TARGET_URI, FROM_URI, branch_id, 5,
+			  &pjsip_options_method, 
+			  request_cnt, 1000, 1);
+    if (status && status != TEST_TIMEOUT_ERROR)
+	return status;
+    if (!status) {
+	PJ_LOG(3,(THIS_FILE, "   error: expecting timeout"));
+	return -31;
+    }
+
+    terminate_our_tsx(status_code);
+    flush_events(100);
+
+    if (test_complete != 1)
+	return test_complete;
+
+    flush_events(100);
+    return 0;
+}
+
+/*****************************************************************************
+ **
+ ** TEST7_BRANCH_ID: INVITE non-2xx final response retransmission test
+ ** TEST8_BRANCH_ID: INVITE 2xx final response retransmission test
+ **
+ *****************************************************************************
+ */
+static int tsx_final_response_retransmission_test(void)
+{
+    int status;
+
+    PJ_LOG(3,(THIS_FILE,
+	      "  test7: INVITE non-2xx final response retransmission"));
+
+    status = perform_test(TARGET_URI, FROM_URI, TEST7_BRANCH_ID,
+			  33, /* Test duration must be greater than 32 secs */
+			  &pjsip_invite_method, 1, 0, 0);
+    if (status != 0)
+	return status;
+
+    PJ_LOG(3,(THIS_FILE,
+	      "  test8: INVITE 2xx final response retransmission"));
+
+    status = perform_test(TARGET_URI, FROM_URI, TEST8_BRANCH_ID,
+			  33, /* Test duration must be greater than 32 secs */
+			  &pjsip_invite_method, 1, 0, 0);
+    if (status != 0)
+	return status;
+
+    return 0;
+}
+
+
+/*****************************************************************************
+ **
+ ** TEST9_BRANCH_ID: retransmission of non-2xx INVITE final response must 
+ ** cease when ACK is received
+ **
+ *****************************************************************************
+ */
+static int tsx_ack_test(void)
+{
+    int status;
+
+    PJ_LOG(3,(THIS_FILE,
+	      "  test9: receiving ACK for non-2xx final response"));
+
+    status = perform_test(TARGET_URI, FROM_URI, TEST9_BRANCH_ID,
+			  20, /* allow 5 retransmissions */
+			  &pjsip_invite_method, 1, 0, 0);
+    if (status != 0)
+	return status;
+
+
+    return 0;
+}
+
+
+
+/*****************************************************************************
+ **
+ ** TEST10_BRANCH_ID: test transport failure in TRYING state.
+ ** TEST11_BRANCH_ID: test transport failure in PROCEEDING state.
+ ** TEST12_BRANCH_ID: test transport failure in CONNECTED state.
+ ** TEST13_BRANCH_ID: test transport failure in CONFIRMED state.
+ **
+ *****************************************************************************
+ */
+static int tsx_transport_failure_test(void)
+{
+    struct test_desc
+    {
+	int transport_delay;
+	int fail_delay;
+	char *branch_id;
+	char *title;
+    } tests[] = 
+    {
+	{ 0,  10,   TEST10_BRANCH_ID, "test10: failed transport in TRYING state (no delay)" },
+	{ 50, 10,   TEST10_BRANCH_ID, "test10: failed transport in TRYING state (50 ms delay)" },
+	{ 0,  1500, TEST11_BRANCH_ID, "test11: failed transport in PROCEEDING state (no delay)" },
+	{ 50, 1500, TEST11_BRANCH_ID, "test11: failed transport in PROCEEDING state (50 ms delay)" },
+	{ 0,  2500, TEST12_BRANCH_ID, "test12: failed transport in COMPLETED state (no delay)" },
+	{ 50, 2500, TEST12_BRANCH_ID, "test12: failed transport in COMPLETED state (50 ms delay)" },
+    };
+    int i, status;
+
+    for (i=0; i<(int)PJ_ARRAY_SIZE(tests); ++i) {
+	pj_time_val fail_time, end_test, now;
+
+	PJ_LOG(3,(THIS_FILE, "  %s", tests[i].title));
+	pjsip_loop_set_failure(loop, 0, NULL);
+	pjsip_loop_set_delay(loop, tests[i].transport_delay);
+
+	status = perform_test(TARGET_URI, FROM_URI, tests[i].branch_id,
+			      0, &pjsip_invite_method, 1, 0, 1);
+	if (status && status != TEST_TIMEOUT_ERROR)
+	    return status;
+	if (!status) {
+	    PJ_LOG(3,(THIS_FILE, "   error: expecting timeout"));
+	    return -40;
+	}
+
+	pj_gettimeofday(&fail_time);
+	fail_time.msec += tests[i].fail_delay;
+	pj_time_val_normalize(&fail_time);
+
+	do {
+	    pj_time_val interval = { 0, 1 };
+	    pj_gettimeofday(&now);
+	    pjsip_endpt_handle_events(endpt, &interval);
+	} while (PJ_TIME_VAL_LT(now, fail_time));
+
+	pjsip_loop_set_failure(loop, 1, NULL);
+
+	end_test = now;
+	end_test.sec += 5;
+
+	do {
+	    pj_time_val interval = { 0, 1 };
+	    pj_gettimeofday(&now);
+	    pjsip_endpt_handle_events(endpt, &interval);
+	} while (!test_complete && PJ_TIME_VAL_LT(now, end_test));
+
+	if (test_complete == 0) {
+	    PJ_LOG(3,(THIS_FILE, "   error: test has timed out"));
+	    return -41;
+	}
+
+	if (test_complete != 1)
+	    return test_complete;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************
+ **
+ ** UAS Transaction Test.
+ **
+ *****************************************************************************
+ */
+int tsx_uas_test(struct tsx_test_param *param)
+{
+    pj_sockaddr_in addr;
+    pj_status_t status;
+
+    test_param = param;
+    tp_flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)param->type);
+
+    pj_ansi_sprintf(TARGET_URI, "sip:bob@127.0.0.1:%d;transport=%s", 
+		    param->port, param->tp_type);
+    pj_ansi_sprintf(FROM_URI, "sip:alice@127.0.0.1:%d;transport=%s", 
+		    param->port, param->tp_type);
+
+    /* Check if loop transport is configured. */
+    status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, 
+				      &addr, sizeof(addr), NULL, &loop);
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(3,(THIS_FILE, "  Error: loop transport is not configured!"));
+	return -10;
+    }
+    /* Register modules. */
+    status = pjsip_endpt_register_module(endpt, &tsx_user);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to register module", status);
+	return -3;
+    }
+    status = pjsip_endpt_register_module(endpt, &msg_sender);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to register module", status);
+	return -4;
+    }
+
+    /* TEST1_BRANCH_ID: Basic 2xx final response. 
+     * TEST2_BRANCH_ID: Basic non-2xx final response. 
+     */
+    status = tsx_basic_final_response_test();
+    if (status != 0)
+	return status;
+
+    /* TEST3_BRANCH_ID: with provisional response
+     */
+    status = tsx_basic_provisional_response_test();
+    if (status != 0)
+	return status;
+
+    /* TEST4_BRANCH_ID: absorbs retransmissions in TRYING state
+     */
+    status = tsx_retransmit_last_response_test(TEST4_TITLE,
+					       TEST4_BRANCH_ID, 
+					       TEST4_REQUEST_COUNT,
+					       TEST4_STATUS_CODE);
+    if (status != 0)
+	return status;
+
+    /* TEST5_BRANCH_ID: retransmit last response in PROCEEDING state
+     */
+    status = tsx_retransmit_last_response_test(TEST5_TITLE,
+					       TEST5_BRANCH_ID, 
+					       TEST5_REQUEST_COUNT,
+					       TEST5_STATUS_CODE);
+    if (status != 0)
+	return status;
+
+    /* TEST6_BRANCH_ID: retransmit last response in COMPLETED state
+     *                  This only applies to non-reliable transports,
+     *			since UAS transaction is destroyed as soon
+     *			as final response is sent for reliable transports.
+     */
+    if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) {
+	status = tsx_retransmit_last_response_test(TEST6_TITLE,
+						   TEST6_BRANCH_ID, 
+						   TEST6_REQUEST_COUNT,
+						   TEST6_STATUS_CODE);
+	if (status != 0)
+	    return status;
+    }
+
+    /* TEST7_BRANCH_ID: INVITE non-2xx final response retransmission test
+     * TEST8_BRANCH_ID: INVITE 2xx final response retransmission test
+     */
+    status = tsx_final_response_retransmission_test();
+    if (status != 0)
+	return status;
+
+    /* TEST9_BRANCH_ID: retransmission of non-2xx INVITE final response must 
+     * cease when ACK is received
+     * Only applicable for non-reliable transports.
+     */
+    if ((tp_flag & PJSIP_TRANSPORT_RELIABLE) == 0) {
+	status = tsx_ack_test();
+	if (status != 0)
+	    return status;
+    }
+
+
+    /* TEST10_BRANCH_ID: test transport failure in TRYING state.
+     * TEST11_BRANCH_ID: test transport failure in PROCEEDING state.
+     * TEST12_BRANCH_ID: test transport failure in CONNECTED state.
+     * TEST13_BRANCH_ID: test transport failure in CONFIRMED state.
+     */
+    /* Only valid for loop-dgram */
+    if (param->type == PJSIP_TRANSPORT_LOOP_DGRAM) {
+	status = tsx_transport_failure_test();
+	if (status != 0)
+	    return status;
+    }
+
+
+    /* Register modules. */
+    status = pjsip_endpt_unregister_module(endpt, &tsx_user);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to unregister module", status);
+	return -8;
+    }
+    status = pjsip_endpt_unregister_module(endpt, &msg_sender);
+    if (status != PJ_SUCCESS) {
+	app_perror("   Error: unable to unregister module", status);
+	return -9;
+    }
+
+
+    if (loop)
+	pjsip_transport_dec_ref(loop);
+
+    return 0;
+}
+