test.tck.msgflow.InviteClientTransactionsStateMachineTest Maven / Gradle / Ivy
/*
* 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