All Downloads are FREE. Search and download functionalities are using the official Maven repository.

test.tck.msgflow.InviteClientTransactionsStateMachineTest Maven / Gradle / Ivy

There is a newer version: 1.3.0-91
Show newest version
/*
 * Conditions Of Use
 *
 * This software was developed by employees of the National Institute of
 * Standards and Technology (NIST), and others.
 * This software is has been contributed to the public domain.
 * As a result, a formal license is not needed to use the software.
 *
 * This software is provided "AS IS."
 * NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
 * AND DATA ACCURACY.  NIST does not warrant or make any representations
 * regarding the use of the software or the results thereof, including but
 * not limited to the correctness, accuracy, reliability or usefulness of
 * the software.
 *
 *
 */
package test.tck.msgflow;

import java.util.TooManyListenersException;

import javax.sip.*;
import javax.sip.header.ContactHeader;
import javax.sip.header.ToHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.apache.log4j.Logger;

import junit.framework.Test;
import junit.framework.TestSuite;
import test.tck.TckInternalError;
import test.tck.TiUnexpectedError;

/**
 * The test tries to verify that Invite Client Transactions correctly change
 * states as specified by the rfc3261. The Tested Implementation is used to send
 * requests and the ReferenceImplementation issues (or not) corresponding
 * responses. ClientTransaction states are constantly queried and compared to
 * those in the state machine described in section 17.1.1 of rfc3261
 *
 * 
 *
 *                               |INVITE from TU
 *             Timer A fires     |INVITE sent
 *             Reset A,          V                      Timer B fires
 *             INVITE sent +-----------+                or Transport Err.
 *               +---------|           |---------------+inform TU
 *               |         |  Calling  |               |
 *               +-------->|           |-------------->|
 *                         +-----------+ 2xx           |
 *                            |  |       2xx to TU     |
 *                            |  |1xx                  |
 *    300-699 +---------------+  |1xx to TU            |
 *   ACK sent |                  |                     |
 * esp. to TU |  1xx             V                     |
 *            |  1xx to TU  -----------+               |
 *            |  +---------|           |               |
 *            |  |         |Proceeding |-------------->|
 *            |  +-------->|           | 2xx           |
 *            |            +-----------+ 2xx to TU     |
 *            |       300-699    |                     |
 *            |       ACK sent,  |                     |
 *            |       resp. to TU|                     |
 *            |                  |                     |      NOTE:
 *            |  300-699         V                     |
 *            |  ACK sent  +-----------+Transport Err. |  transitions
 *            |  +---------|           |Inform TU      |  labeled with
 *            |  |         | Completed |-------------->|  the event
 *            |  +-------->|           |               |  over the action
 *            |            +-----------+               |  to take
 *            |              ˆ   |                     |
 *            |              |   | Timer D fires       |
 *            +--------------+   | -                   |
 *                               |                     |
 *                               V                     |
 *                         +-----------+               |
 *                         |           |               |
 *                         | Terminated|<--------------+
 *                         |           |
 *                         +-----------+
 *
 *                 Figure 5: INVITE client transaction
 *
 *
 * 
* * TODO Currently, transactions' behaviour is not tested for misappropriation of * incoming messages. * * TODO Currently, invite transactions are not tested for proper termination * upon respective timeouts. * * @author Emil Ivov Network Research Team, Louis Pasteur University, * Strasbourg, France. This code is in the public domain. * @version 1.0 */ public class InviteClientTransactionsStateMachineTest extends MessageFlowHarness { private static Logger logger = Logger .getLogger(InviteClientTransactionsStateMachineTest.class); public InviteClientTransactionsStateMachineTest(String name) { super(name, false); // disable auto-dialog for the RI, else ACKs get // filtered out } // ==================== tests ============================== /** * Tries to steer a TI client transaction through the following scenario * Calling-->Proceeding-->Completed-->Terminated. Apart from state * transitions, we also test, retransmissions and proper hiding/passing of * messages to the TU. */ public void testCallingProceedingCompletedTerminatedScenario() { try { Request invite = createTiInviteRequest(null, null, null); ClientTransaction tran = null; try { eventCollector.collectRequestEvent(riSipProvider); tran = tiSipProvider.getNewClientTransaction(invite); tran.sendRequest(); } catch (SipException ex) { throw new TiUnexpectedError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector .extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError( "The invite request was not received by the RI!"); // At this point the ClientTransaction should be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Check Request retransmission try { eventCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } // Wait for the retransmission timer to fire if it had not already // done so. if (tran.getRetransmitTimer() > MESSAGES_ARRIVE_FOR) sleep((long) tran.getRetransmitTimer() - MESSAGES_ARRIVE_FOR); // subtract // the // time // we // waited // for // the // invite // Wait for the retransmitted request to arrive waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent.getRequest()); assertEquals(Request.INVITE, inviteReceivedEvent.getRequest() .getMethod()); // At this point the ClientTransaction should STILL be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Send a TRYING response try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } try { Response resp = riMessageFactory.createResponse( Response.TRYING, inviteReceivedEvent.getRequest()); addStatus(inviteReceivedEvent.getRequest(), resp); riSipProvider.sendResponse(resp); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a trying response back to the TI", ex); } waitForMessage(); // Analyze the TRYING response and Tran state back at the TI ResponseEvent responseEvent = eventCollector .extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 1xx response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 1xx response to the TU!", responseEvent.getResponse()); assertTrue( "A response different from TYING was passed to the TU!", responseEvent.getResponse().getStatusCode() == Response.TRYING); assertSame( "The TRYING response was not associated with the right transaction.", tran, responseEvent.getClientTransaction()); // verify the the tran state is now PROCEEDING assertEquals( "The ClientTransaction did not pass in the PROCEEDING state after " + "receiving 1xx provisional response", tran .getState(), TransactionState.PROCEEDING); // Send a 486 BUSY HERE (final) response from the RI try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // The BUSY_HERE response should trigger some ACKs so let's register // a listener with the RI SipEventCollector ackCollector = new SipEventCollector(); try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } Response busyHere = null; try { busyHere = riMessageFactory.createResponse(Response.BUSY_HERE, inviteReceivedEvent.getRequest()); addStatus(inviteReceivedEvent.getRequest(), busyHere); // JvB: set to-tag too, mandatory and tests that ACK is properly // formatted ((ToHeader) busyHere.getHeader("to")).setTag("busy-here"); riSipProvider.sendResponse(busyHere); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a BUSY HERE response back to the TI", ex); } waitForMessage(); // Analyze the BUSY_HERE response and Tran state back at the TI responseEvent = eventCollector.extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 300-699 response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 300-699 response to the TU!", responseEvent.getResponse()); assertSame( "The BUSY_HERE response was not associated with the right transaction", tran, responseEvent.getClientTransaction()); assertEquals( "A response different from BUSY_HERE was passed to the TU", Response.BUSY_HERE, responseEvent.getResponse() .getStatusCode()); assertEquals( "The ClientTransaction did not pass in the COMPLETED state after " + "receiving 300-699 final response", tran .getState(), TransactionState.COMPLETED); // check whether the ackCollector has caught any fish RequestEvent ackReceivedEvent = ackCollector .extractCollectedRequestEvent(); // JvB: With auto-dialog-support enabled, the ACK should be filtered // by the RI assertNotNull("The TI did not send an ACK request", ackReceivedEvent); assertNotNull("The TI did not send an ACK request", ackReceivedEvent.getRequest()); assertEquals(Request.ACK, ackReceivedEvent.getRequest().getMethod()); // Try to kill remaining ACK retransmissions // TODO this may not always work .. should give it a specific // timeout value waitForMessage(); // Now let's retransmit the final response. This time it shouldn't // be // passed to the TU but an ACK should still be sent try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // go fish the ack try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } try { riSipProvider.sendResponse((Response) busyHere.clone()); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a BUSY HERE response back to the TI", ex); } waitForMessage(); // The TU shouldn't see the retransmitted BUSY_HERE response responseEvent = eventCollector.extractCollectedResponseEvent(); assertNull( "The Tested Implementation passed a retransmitted 300-699 response " + "to the TU instead of just silently acknowledging it!", responseEvent); // We must still be in the completed state. assertEquals( "The ClientTransaction did not stay long enough in the COMPLETED " + "state.", tran.getState(), TransactionState.COMPLETED); // check whether the ackCollector has caught any fish ackReceivedEvent = ackCollector.extractCollectedRequestEvent(); assertNotNull( "The TI did not send an ACK request to the second response", ackReceivedEvent); assertNotNull( "The TI did not send an ACK request to the second response", ackReceivedEvent.getRequest()); assertEquals(Request.ACK, ackReceivedEvent.getRequest().getMethod()); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); // Unfortunately we can't assert the TERMINATED state as timerK timerD // is not exported by JAIN SIP } /** * JvB: Tests transmission of an INVITE followed by CANCELlation of that * request * -> INVITE <- 100 -> CANCEL <- OK <- 487 -> ACK */ public void testInviteCancel() { doCancelTest(true); } /** * Same as above, but for non-RFC3261 client (i.e. no Via branch, no from/to * tags * * Cannot do this on 1.2 stack * * public void testInviteCancelNonRFC3261() { doCancelTest(false); } */ /** * JvB: Tests transmission of an INVITE followed by CANCELlation of that * request * -> INVITE <- 100 -> CANCEL <- OK <- 487 -> ACK * * Note: for 1.2 it is impossible to manually set the top via branch to * something not RFC3261-compliant */ private void doCancelTest(boolean rfc3261Compliant) { try { Request invite = createTiInviteRequest(null, null, null); ClientTransaction tran = null; try { eventCollector.collectRequestEvent(riSipProvider); // This call overwrites any branch we set tran = tiSipProvider.getNewClientTransaction(invite); // And this call too tran.sendRequest(); } catch (SipException ex) { throw new TiUnexpectedError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector .extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError( "The invite request was not received by the RI!"); // At this point the ClientTransaction should be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Send a TRYING response try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } try { Response resp = riMessageFactory.createResponse( Response.TRYING, inviteReceivedEvent.getRequest()); addStatus(inviteReceivedEvent.getRequest(), resp); riSipProvider.sendResponse(resp); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a trying response back to the TI", ex); } waitForMessage(); // Analyze the TRYING response and Tran state back at the TI ResponseEvent responseEvent = eventCollector .extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 1xx response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 1xx response to the TU!", responseEvent.getResponse()); assertTrue( "A response different from TYING was passed to the TU!", responseEvent.getResponse().getStatusCode() == Response.TRYING); assertSame( "The TRYING response was not associated with the right transaction.", tran, responseEvent.getClientTransaction()); // verify the the tran state is now PROCEEDING assertEquals( "The ClientTransaction did not pass in the PROCEEDING state after " + "receiving 1xx provisional response", tran .getState(), TransactionState.PROCEEDING); // Send a CANCEL from the TI Request tiCancel = tran.createCancel(); /* * this works, but since we cannot patch the INVITE the test fails * if (!rfc3261Compliant) { ((ViaHeader) * tiCancel.getHeader("Via")).setBranch( "xxx" ); // Not allowed by * RI // ((FromHeader) tiCancel.getHeader("From")).setTag( "" ); } */ ClientTransaction tiCancelTrans; try { eventCollector.collectRequestEvent(riSipProvider); tiCancelTrans = tiSipProvider.getNewClientTransaction(tiCancel); tiCancelTrans.sendRequest(); } catch (SipException ex) { throw new TiUnexpectedError( "A SipExceptionOccurred while trying to send CANCEL!", ex); } waitForMessage(); RequestEvent cancelReceivedEvent = eventCollector .extractCollectedRequestEvent(); if (cancelReceivedEvent == null || cancelReceivedEvent.getRequest() == null) throw new TiUnexpectedError( "The CANCEL request was not received by the RI!"); // Send 200 OK to the CANCEL try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } Response riCancelOk = null; try { riCancelOk = riMessageFactory.createResponse(Response.OK, cancelReceivedEvent.getRequest()); addStatus(cancelReceivedEvent.getRequest(), riCancelOk); riSipProvider.sendResponse(riCancelOk); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a CANCEL OK response back to the TI", ex); } waitForMessage(); // Analyze the OK response and Tran state back at the TI responseEvent = eventCollector.extractCollectedResponseEvent(); if (responseEvent == null || responseEvent.getResponse() == null) { throw new TiUnexpectedError( "The CANCEL OK response was not received by the TI!"); } // Send 487 to the INVITE, expect ACK try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } SipEventCollector ackCollector = new SipEventCollector(); try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } Response riInviteTerminated = null; try { riInviteTerminated = riMessageFactory.createResponse( Response.REQUEST_TERMINATED, inviteReceivedEvent .getRequest()); addStatus(inviteReceivedEvent.getRequest(), riInviteTerminated); riSipProvider.sendResponse(riInviteTerminated); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a INVITE 487 response back to the TI", ex); } waitForMessage(); // Analyze the REQUEST_TERMINATED response and Tran state back at // the TI responseEvent = eventCollector.extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 300-699 response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 300-699 response to the TU!", responseEvent.getResponse()); assertSame( "The 487 response was not associated with the right transaction", tran, responseEvent.getClientTransaction()); assertEquals("A response different from 487 was passed to the TU", Response.REQUEST_TERMINATED, responseEvent.getResponse() .getStatusCode()); assertEquals( "The ClientTransaction did not pass in the COMPLETED state after " + "receiving 300-699 final response", tran .getState(), TransactionState.COMPLETED); // check whether the ackCollector has caught any fish RequestEvent ackReceivedEvent = ackCollector .extractCollectedRequestEvent(); assertNotNull("The TI did not send an ACK request event", ackReceivedEvent); assertNotNull("The TI did not send an ACK request", ackReceivedEvent.getRequest()); assertEquals(Request.ACK, ackReceivedEvent.getRequest().getMethod()); // Try to kill remaining ACK retransmissions? } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); // Unfortunately we can't assert the TERMINATED state as timerK timerD // is not exported by JAIN SIP } /** * Tries to steer a TI client transaction through the following scenario * Calling-->Completed-->Terminated. Apart from state transitions, we also * test, retransmissions and proper hiding/passing of messages to the TU. */ public void testCallingCompletedTerminatedScenario() { try { Request invite = createTiInviteRequest(null, null, null); // JvB: test // ((MessageExt) invite).getFirstViaHeader().setParameter( "rport", // null ); ClientTransaction tran = null; try { eventCollector.collectRequestEvent(riSipProvider); tran = tiSipProvider.getNewClientTransaction(invite); tran.sendRequest(); } catch (SipException ex) { throw new TiUnexpectedError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector .extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError( "The invite request was not received by the RI!"); // At this point the ClientTransaction should be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Check Request retransmission try { eventCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } // Wait for the retransmission timer to fire if it had not already // done so. if (tran.getRetransmitTimer() > MESSAGES_ARRIVE_FOR) sleep((long) tran.getRetransmitTimer() - MESSAGES_ARRIVE_FOR); // subtract // the // time // we // waited // for // the // invite // Wait for the retransmitted request to arrive waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent.getRequest()); assertEquals(Request.INVITE, inviteReceivedEvent.getRequest() .getMethod()); // At this point the ClientTransaction should STILL be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Send a 486 BUSY HERE (final) response from the RI try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // The BUSY_HERE response should trigger some ACKs so let's register // a listener with the RI SipEventCollector ackCollector = new SipEventCollector(); try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } Response busyHere = null; try { busyHere = riMessageFactory.createResponse(Response.BUSY_HERE, inviteReceivedEvent.getRequest()); addStatus(inviteReceivedEvent.getRequest(), busyHere); // JvB: set to-tag, check it against the ACK generated ((ToHeader) busyHere.getHeader("to")).setTag("ack-to-test"); riSipProvider.sendResponse((Response) busyHere.clone()); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a BUSY HERE response back to the TI", ex); } waitForMessage(); // Analyze the BUSY_HERE response and Tran state back at the TI ResponseEvent responseEvent = eventCollector .extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 300-699 response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 300-699 response to the TU!", responseEvent.getResponse()); assertSame( "The BUSY_HERE response was not associated with the right transaction", tran, responseEvent.getClientTransaction()); assertSame( "A response different from BUSY_HERE was passed to the TU", tran, responseEvent.getClientTransaction()); assertEquals( "The ClientTransaction did not pass in the COMPLETED state after " + "receiving 300-699 final response", tran .getState(), TransactionState.COMPLETED); // check whether the ackCollector has caught any fish RequestEvent ackReceivedEvent = ackCollector .extractCollectedRequestEvent(); assertNotNull("The TI did not send an ACK request", ackReceivedEvent); assertNotNull("The TI did not send an ACK request", ackReceivedEvent.getRequest()); assertEquals(Request.ACK, ackReceivedEvent.getRequest().getMethod()); // Try to kill remaining ACK retransmissions // TODO this may not always work .. should give it a specific // timeout value waitForMessage(); // Now let's retransmit the final response. This time it shouldn't // be // passed to the TU but an ACK should still be sent try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // go fish the ack try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } try { riSipProvider.sendResponse((Response) busyHere.clone()); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a BUSY HERE response back to the TI", ex); } waitForMessage(); // The TU shouldn't see the retransmitted BUSY_HERE response responseEvent = eventCollector.extractCollectedResponseEvent(); assertNull( "The Tested Implementation passed a retransmitted 300-699 response " + "to the TU instead of just silently acknowledging it!", responseEvent); // We must still be in the completed state. assertEquals( "The ClientTransaction did not stay long enough in the COMPLETED " + "state.", tran.getState(), TransactionState.COMPLETED); // check whether the ackCollector has caught any fish ackReceivedEvent = ackCollector.extractCollectedRequestEvent(); assertNotNull( "The TI did not send an ACK request to the second response", ackReceivedEvent); assertNotNull( "The TI did not send an ACK request to the second response", ackReceivedEvent.getRequest()); assertEquals(Request.ACK, ackReceivedEvent.getRequest().getMethod()); // JvB: verify to tag assertEquals( "The To header field in the ACK MUST equal the To header field " + " in the response being acknowledged", "ack-to-test", ((ToHeader) ackReceivedEvent.getRequest() .getHeader("to")).getTag()); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); // Unfortunately we can't assert the TERMINATED state as timerD // is not exported by JAIN SIP } /** * Tries to steer a TI client transaction through the following scenario * Calling-->Proceeding-->Terminated. Apart from state transitions, we also * test, retransmissions and proper hiding/passing of messages to the TU. */ public void testCallingProceedingTerminatedScenario() { try { Request invite = createTiInviteRequest(null, null, null); ClientTransaction tran = null; try { eventCollector.collectRequestEvent(riSipProvider); tran = tiSipProvider.getNewClientTransaction(invite); tran.sendRequest(); } catch (SipException ex) { throw new TiUnexpectedError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector .extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError( "The invite request was not received by the RI!"); // At this point the ClientTransaction should be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Check Request retransmission try { eventCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } // Wait for the retransmission timer to fire if it had not already // done so. if (tran.getRetransmitTimer() > MESSAGES_ARRIVE_FOR) sleep((long) tran.getRetransmitTimer() - MESSAGES_ARRIVE_FOR); // subtract // the // time // we // waited // for // the // invite // Wait for the retransmitted request to arrive waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent.getRequest()); assertEquals(Request.INVITE, inviteReceivedEvent.getRequest() .getMethod()); // At this point the ClientTransaction should STILL be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Send a TRYING response try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } try { Response resp = riMessageFactory.createResponse( Response.TRYING, inviteReceivedEvent.getRequest()); addStatus(inviteReceivedEvent.getRequest(), resp); riSipProvider.sendResponse(resp); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send a trying response back to the TI", ex); } waitForMessage(); // Analyze the TRYING response and Tran state back at the TI ResponseEvent responseEvent = eventCollector .extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 1xx response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 1xx response to the TU!", responseEvent.getResponse()); assertTrue( "A response different from TYING was passed to the TU!", responseEvent.getResponse().getStatusCode() == Response.TRYING); assertSame( "The TRYING response was not associated with the right transaction", tran, responseEvent.getClientTransaction()); // verify the the tran state is now PROCEEDING assertEquals( "The ClientTransaction did not pass in the PROCEEDING state after " + "receiving 1xx provisional response", tran .getState(), TransactionState.PROCEEDING); // Send a 200 OK (final) response from the RI try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // The OK response shouldn't trigger any ACKs so let's register // a listener with the RI to verify whether that is the case SipEventCollector ackCollector = new SipEventCollector(); try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } Response ok = null; try { ok = riMessageFactory.createResponse(Response.OK, inviteReceivedEvent.getRequest()); // JvB: MUST add Contact too! ContactHeader contact = riHeaderFactory .createContactHeader(((ToHeader) ok.getHeader("To")) .getAddress()); ok.addHeader(contact); // end JvB addStatus(inviteReceivedEvent.getRequest(), ok); riSipProvider.sendResponse((Response) ok.clone()); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send an OK response back to the TI", ex); } waitForMessage(); // Analyze the OK response and Tran state back at the TI responseEvent = eventCollector.extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 200 OK response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 200 OK response to the TU!", responseEvent.getResponse()); assertSame( "The OK response was not associated with the right transaction", tran, responseEvent.getClientTransaction()); assertSame("A response different from OK was passed to the TU", tran, responseEvent.getClientTransaction()); assertEquals( "The ClientTransaction did not pass in the TERMINATED state after " + "receiving 200 final response", tran.getState(), TransactionState.TERMINATED); // check whether the ackCollector has caught any fish RequestEvent ackReceivedEvent = ackCollector .extractCollectedRequestEvent(); if (ackReceivedEvent != null) logger.error("Shouldn't have received that=" + ackReceivedEvent.getRequest()); assertNull("The TI sent an ACK to an OK (this is TU's job)!", ackReceivedEvent); // Now let's retransmit the final response and see it is // passed to the TU (again no ACKs) try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // go fish the ack try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } try { riSipProvider.sendResponse((Response) ok.clone()); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send an OK response back to the TI", ex); } waitForMessage(); // Did we get the 2nd OK? responseEvent = eventCollector.extractCollectedResponseEvent(); /* * JvB: the stack should in fact ~not~ pass the retransmitted * response, but filter it out * * assertNotNull( "The TI did not pass to the TU a retransmitted OK * response!", responseEvent); assertNotNull( "The TI did not pass * to the TU a retransmitted OK response!", * responseEvent.getResponse()); assertTrue( "The TI passed to the * TU a bad response!", * responseEvent.getResponse().getStatusCode()==Response.OK); */ // TBD // assertNull( "The TI should filter the retransmitted OK response", // responseEvent ); // We must still be in the terminated state. assertEquals( "The ClientTransaction mysteriously left the TERMINATED state!", tran.getState(), TransactionState.TERMINATED); // check whether the ackCollector has caught any fish ackReceivedEvent = ackCollector.extractCollectedRequestEvent(); assertNull("The TI sent an ACK request to the second OK response " + "(OK acks are TU's responsibility)!", ackReceivedEvent); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); } /** * Tries to steer a TI client transaction through the following scenario * Calling-->Terminated. Apart from state transitions, we also test, * retransmissions and proper hiding/passing of messages to the TU. */ public void testCallingTerminatedScenario() { try { Request invite = createTiInviteRequest(null, null, null); ClientTransaction tran = null; try { eventCollector.collectRequestEvent(riSipProvider); tran = tiSipProvider.getNewClientTransaction(invite); tran.sendRequest(); } catch (SipException ex) { throw new TiUnexpectedError( "A SipExceptionOccurred while trying to send request!", ex); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } waitForMessage(); RequestEvent inviteReceivedEvent = eventCollector .extractCollectedRequestEvent(); if (inviteReceivedEvent == null || inviteReceivedEvent.getRequest() == null) throw new TiUnexpectedError( "The invite request was not received by the RI!"); // At this point the ClientTransaction should be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Check Request retransmission try { eventCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } // Wait for the retransmission timer to fire if it had not already // done so. if (tran.getRetransmitTimer() > MESSAGES_ARRIVE_FOR) sleep((long) tran.getRetransmitTimer() - MESSAGES_ARRIVE_FOR); // subtract // the // time // we // waited // for // the // invite // Wait for the retransmitted request to arrive waitForMessage(); inviteReceivedEvent = eventCollector.extractCollectedRequestEvent(); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent); assertNotNull("The invite request was not retransmitted!", inviteReceivedEvent.getRequest()); assertEquals(Request.INVITE, inviteReceivedEvent.getRequest() .getMethod()); // At this point the ClientTransaction should STILL be CALLING! assertEquals(TransactionState.CALLING, tran.getState()); // Send a 200 OK (final) response from the RI try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // The OK response shouldn't trigger any ACKs so let's register // a listener with the RI to verify whether that is the case SipEventCollector ackCollector = new SipEventCollector(); try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } Response ok = null; try { ok = riMessageFactory.createResponse(Response.OK, inviteReceivedEvent.getRequest()); addStatus(inviteReceivedEvent.getRequest(), ok); // JvB: MUST add Contact too! ContactHeader contact = riHeaderFactory .createContactHeader(((ToHeader) ok.getHeader("To")) .getAddress()); ok.addHeader(contact); // end JvB riSipProvider.sendResponse(ok); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send an OK response back to the TI", ex); } waitForMessage(); // Analyze the OK response and Tran state back at the TI ResponseEvent responseEvent = eventCollector .extractCollectedResponseEvent(); assertNotNull( "The Tested Implementation did not pass a 200 OK response to the TU!", responseEvent); assertNotNull( "The Tested Implementation did not pass a 200 OK response to the TU!", responseEvent.getResponse()); assertSame( "The OK response was not associated with the right transaction", tran, responseEvent.getClientTransaction()); assertSame("A response different from OK was passed to the TU", tran, responseEvent.getClientTransaction()); assertEquals( "The ClientTransaction did not pass in the TERMINATED state after " + "receiving 200 final response", tran.getState(), TransactionState.TERMINATED); // check whether the ackCollector has caught any fish RequestEvent ackReceivedEvent = ackCollector .extractCollectedRequestEvent(); assertNull("The TI sent an ACK to an OK (this is TU's job)!", ackReceivedEvent); // Now let's retransmit the final response and see that it is // passed to the TU (again no ACKs should be sent by the TI) try { eventCollector.collectResponseEvent(tiSipProvider); } catch (TooManyListenersException ex) { throw new TiUnexpectedError( "Failed to register a SipListener with TI", ex); } // go fish the ack try { ackCollector.collectRequestEvent(riSipProvider); } catch (TooManyListenersException ex) { throw new TckInternalError( "Failed to regiest a SipListener with an RI SipProvider", ex); } try { riSipProvider.sendResponse((Response) ok.clone()); } catch (Throwable ex) { throw new TckInternalError( "The TCK could not send an OK response back to the TI", ex); } waitForMessage(); // Did we get the 2nd OK? responseEvent = eventCollector.extractCollectedResponseEvent(); /* * JvB: see previous comment * * assertNotNull( "The TI did not pass to the TU a retransmitted OK * response!", responseEvent); assertNotNull( "The TI did not pass * to the TU a retransmitted OK response!", * responseEvent.getResponse()); assertTrue( "The TI passed to the * TU a bad response!", * responseEvent.getResponse().getStatusCode()==Response.OK); */ // JvB: TBD // assertNull( "The TI should filter the retransmitted OK response", // responseEvent ); // We must still be in the terminated state. assertEquals( "The ClientTransaction mysteriously left the TERMINATED state!", tran.getState(), TransactionState.TERMINATED); // check whether the ackCollector has caught any fish ackReceivedEvent = ackCollector.extractCollectedRequestEvent(); assertNull("The TI sent an ACK request to the second OK response " + "(OK acks are TU's responsibility)!", ackReceivedEvent); } catch (Throwable exc) { exc.printStackTrace(); fail(exc.getClass().getName() + ": " + exc.getMessage()); } assertTrue(new Exception().getStackTrace()[0].toString(), true); } // ==================== end of tests // ====== STATIC JUNIT ========== public static Test suite() { return new TestSuite(InviteClientTransactionsStateMachineTest.class); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy