test.tck.msgflow.DialogStateMachineTest 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 junit.framework.*;
import javax.sip.*;
import javax.sip.message.*;
import javax.sip.header.*;
import java.util.*;
import test.tck.*;
/**
*
* Tests whether the dialog state machine is properly implemented. Dialog state
* machines are far less complex than transaction FSMs. All they do is passively
* change states without producing any automatic responses or retransmissions.
* Therefore we're only testing one scenario per Dialog (one for Server generated
* and one for Client generated dialogs. Distinction is made server and client
* generated scenarios are likely to be navigated by server and client transactions
* respectively and not by a single entity)
*
* Title: TCK
* Description: JAIN SIP 1.1 Technology Compatibility Kit
* @author Emil Ivov
* Network Research Team, Louis Pasteur University, Strasbourg, France.
* This code is in the public domain.
* @version 1.0
*/
public class DialogStateMachineTest extends MessageFlowHarness {
public DialogStateMachineTest(String name) {
super(name,true); // enable auto-dialog support
}
//==================== tests ==============================
/**
* Tests state transitions of a server side dialog
*/
public void testClientDialogStates() {
try {
Request invite = createTiInviteRequest(null, null, null);
ClientTransaction tran = null;
//Send an invite request
try {
eventCollector.collectRequestEvent(riSipProvider);
tran = tiSipProvider.getNewClientTransaction(invite);
tran.sendRequest();
} catch (TooManyListenersException e) {
throw new TckInternalError(
"Failed to register a listener with the RI",
e);
} catch (SipException e) {
throw new TiUnexpectedError(
"Failed to send initial invite request",
e);
}
//Wait for the invite to arrive
waitForMessage();
RequestEvent inviteReqEvt =
eventCollector.extractCollectedRequestEvent();
if (inviteReqEvt == null || inviteReqEvt.getRequest() == null)
throw new TiUnexpectedError("The TI did not send the initial invite request");
//get the dialog
Dialog dialog = tran.getDialog();
//We should have a null state here
assertNull(
"A dialog passed into the "
+ dialog.getState()
+ " state before receiving any response!",
dialog.getState());
//We will now send RINGING response and see that the Dialog enters an early state
//start listening for the response
try {
eventCollector.collectResponseEvent(tiSipProvider);
} catch (TooManyListenersException e) {
throw new TiUnexpectedError(
"Failed to register a SipListener with the TI.",
e);
}
Response ringing = null;
try {
ringing =
riMessageFactory.createResponse(
Response.RINGING,
inviteReqEvt.getRequest());
((ToHeader) ringing.getHeader(ToHeader.NAME)).setTag(
Integer.toString(hashCode()));
// BUG: set contact header on dialog-creating response
ringing.setHeader(createRiContact());
riSipProvider.sendResponse(ringing);
} catch (Exception e) {
throw new TckInternalError(
"Failed to create and send a RINGING response",
e);
}
waitForMessage();
ResponseEvent ringingRespEvt =
eventCollector.extractCollectedResponseEvent();
if (ringingRespEvt == null || ringingRespEvt.getResponse() == null)
throw new TiUnexpectedError("The TI did not pass RINGING response to the TU.");
//The dialog should now be in its early state.
assertEquals(
"The Dialog did not pass into the early state upon reception of a RINGING response",
DialogState.EARLY,
dialog.getState());
// JvB: @todo Test that UPDATE (or PRACK) requests during early dialog
// dont make dialog CONFIRMED
/*
Request update = null;
try {
eventCollector.collectRequestEvent(riSipProvider);
update = dialog.createRequest( "UPDATE" );
update.setHeader(createTiContact());
dialog.sendRequest( tiSipProvider.getNewClientTransaction(update) );
} catch (Exception e) {
throw new TckInternalError(
"Failed to create and send an UPDATE request",
e);
}
waitForMessage();
RequestEvent updateEvt =
eventCollector.extractCollectedRequestEvent();
if (updateEvt == null || updateEvt.getRequest() == null)
throw new TiUnexpectedError("The TI did not send the UPDATE request");
//The dialog should still be in its EARLY state.
assertEquals(
"The Dialog did not stay in the EARLY state upon reception of an UPDATE request",
DialogState.EARLY,
dialog.getState());
// finish the UPDATE
riSipProvider.sendResponse( riMessageFactory.createResponse(200, updateEvt.getRequest()) );
*/
//We will now send OK response and see that the Dialog enters a CONFIRMED state
//start listening for the response
try {
eventCollector.collectResponseEvent(tiSipProvider);
} catch (TooManyListenersException e) {
throw new TiUnexpectedError(
"Failed to register a SipListener with the TI.",
e);
}
Response ok = null;
try {
ok =
riMessageFactory.createResponse(
Response.OK,
inviteReqEvt.getRequest());
((ToHeader) ok.getHeader(ToHeader.NAME)).setTag(
Integer.toString(hashCode()));
// BUG: set contact header on dialog-creating response
ok.setHeader(createRiContact());
riSipProvider.sendResponse(ok);
} catch (Exception e) {
throw new TckInternalError(
"Failed to create and send a OK response",
e);
}
waitForMessage();
ResponseEvent okRespEvt =
eventCollector.extractCollectedResponseEvent();
if (okRespEvt == null || okRespEvt.getResponse() == null)
throw new TiUnexpectedError("The TI did not pass OK response to the TU.");
//The dialog should now be in its confirmed state.
assertEquals(
"The Dialog did not pass into the CONFIRMED state upon reception of an OK response",
DialogState.CONFIRMED,
dialog.getState());
//Say bye and go COMPLETED
Request bye = null;
try {
bye = dialog.createRequest(Request.BYE);
ClientTransaction byeTran =
tiSipProvider.getNewClientTransaction(bye);
dialog.sendRequest(byeTran);
} catch (SipException e) {
throw new TiUnexpectedError(
"Failed to create and send a BYE request using a dialog.",
e);
}
// Send response before checking that the state goes to
// terminated state.
waitForMessage();
tiSipProvider.sendResponse(tiMessageFactory.createResponse(200,bye));
waitForMessage();
assertTrue(
"The dialog did not pass into a final ( TERMINATED) state after getting OK for a BYE.",
DialogState.TERMINATED.equals(dialog.getState()));
} catch (Throwable exc) {
exc.printStackTrace();
fail(exc.getClass().getName() + ": " + exc.getMessage());
}
assertTrue(new Exception().getStackTrace()[0].toString(), true);
}
/**
* Tests state transitions of a client side dialog
*/
public void testServerDialogStates() {
try {
ClientTransaction inviteTransaction = null;
Request invite = createRiInviteRequest(null, null, null);
//Send an invite request
try {
eventCollector.collectRequestEvent(tiSipProvider);
// riSipProvider.sendRequest(invite);
// Made this stateful
inviteTransaction = riSipProvider.getNewClientTransaction(invite);
inviteTransaction.sendRequest();
} catch (TooManyListenersException exc) {
throw new TiUnexpectedError(
"Failed to register a listener with the TI",
exc);
} catch (SipException exc) {
throw new TckInternalError(
"Failed to send initial invite request",
exc);
}
//Wait for the invite to arrive
waitForMessage();
RequestEvent inviteReqEvt =
eventCollector.extractCollectedRequestEvent();
if (inviteReqEvt == null || inviteReqEvt.getRequest() == null)
throw new TiUnexpectedError("The TI did not dispatch the initial invite request");
//Create a transaction
ServerTransaction tran = null;
try {
tran =
tiSipProvider.getNewServerTransaction(
inviteReqEvt.getRequest());
} catch (Exception ex) {
throw new TiUnexpectedError("The TI failed to create a Server transaction for an incoming request");
}
//get the dialog
Dialog dialog = tran.getDialog();
// We should have a null state here
assertNull(
"A dialog passed into the "
+ dialog.getState()
+ " state before sending any response!",
dialog.getState());
//We will now send RINGING response and see that the Dialog enters an early state
//start listening for the response
try {
eventCollector.collectResponseEvent(riSipProvider);
} catch (TooManyListenersException e) {
throw new TckInternalError(
"Failed to register a SipListener with the RI.",
e);
}
Response ringing = null;
try {
ringing =
tiMessageFactory.createResponse(
Response.RINGING,
inviteReqEvt.getRequest());
//!discuss with Ranga
((ToHeader) ringing.getHeader(ToHeader.NAME)).setTag(
Integer.toString(hashCode()));
// BUG report by Ben Evans (Open cloud):
// set contact header on dialog-creating response
ringing.setHeader(createTiContact());
tran.sendResponse(ringing);
} catch (Exception e) {
throw new TiUnexpectedError(
"Failed to create and send a RINGING response",
e);
}
waitForMessage();
ResponseEvent ringingRespEvt =
eventCollector.extractCollectedResponseEvent();
if (ringingRespEvt == null || ringingRespEvt.getResponse() == null)
throw new TiUnexpectedError("The TI did not send the RINGING response.");
//The dialog should now be in its early state.
assertEquals(
"The Dialog did not pass into the early state after sending a RINGING response",
DialogState.EARLY,
dialog.getState());
//We will now send an OK response and see that the Dialog enters a CONFIRMED state
//start listening for the response
SipEventCollector tteCollector = new SipEventCollector();
try {
eventCollector.collectResponseEvent(riSipProvider);
// JvB: Also receive TransactionTerminated event
tteCollector.collectTransactionTermiatedEvent(tiSipProvider);
} catch (TooManyListenersException e) {
throw new TckInternalError(
"Failed to register a SipListener with the RI.",
e);
}
Response ok = null;
try {
ok =
tiMessageFactory.createResponse(
Response.OK,
inviteReqEvt.getRequest());
// Bug - need to set to-tag on OK response
// NIST-SIP fills in the to-tag but this behaviour is
// not specified
((ToHeader) ok.getHeader(ToHeader.NAME)).setTag(
Integer.toString(hashCode()));
ContactHeader contact = createTiContact();
ok.addHeader(contact);
tran.sendResponse(ok);
} catch (Exception e) {
throw new TiUnexpectedError(
"Failed to create and send an OK response",
e);
}
waitForMessage();
ResponseEvent okRespEvt =
eventCollector.extractCollectedResponseEvent();
if (okRespEvt == null || okRespEvt.getResponse() == null)
throw new TiUnexpectedError("The TI did not send an OK response.");
ClientTransaction ct = okRespEvt.getClientTransaction();
// JvB: With auto-dialog-support OFF, this returns *null* !
Dialog clientDialog = ct.getDialog();
assertNotNull( clientDialog );
Request ackReq = clientDialog.createAck( ( (CSeqHeader)okRespEvt.getResponse().getHeader(CSeqHeader.NAME)).getSeqNumber());;
clientDialog.sendAck(ackReq);
waitForMessage();
//The dialog should now be in its CONFIRMED state.
assertEquals(
"The Dialog did not pass into the CONFIRMED state upon reception of an OK response",
DialogState.CONFIRMED,
dialog.getState());
// After sending 2xx, there should be a ServerTransactionTerminated event at UAS side
waitForTimeout(); // May take up to 64*T1 seconds
TransactionTerminatedEvent tte = tteCollector.extractCollectedTransactionTerminatedEvent();
assertNotNull( tte );
assertTrue( tte.isServerTransaction() );
assertEquals( tran, tte.getServerTransaction() );
//Say bye from the RI and see that TI goes COMPLETED
//it is the ri that should say bye here as we are testing dialog navigation
//by server transactions
try {
eventCollector.collectRequestEvent(tiSipProvider);
} catch (TooManyListenersException ex) {
throw new TiUnexpectedError(
"Failed to register a SipListener with the TI",
ex);
}
try {
// Ranga - use the dialog here.
Dialog d = inviteTransaction.getDialog();
Request bye = d.createRequest(Request.BYE);
ct = riSipProvider.getNewClientTransaction(bye);
d.sendRequest(ct);
} catch (Exception e) {
throw new TckInternalError(
"Failed to create and send a BYE request using a dialog.",
e);
}
waitForMessage();
RequestEvent byeEvt = eventCollector.extractCollectedRequestEvent();
if (byeEvt == null || byeEvt.getRequest() == null)
throw new TiUnexpectedError("The TI did not dispatch a BYE request");
ServerTransaction byeTran = null;
/** This should be in the transaction
try
{
byeTran = tiSipProvider.getNewServerTransaction(byeEvt.getRequest());
} catch (TransactionUnavailableException ex) {
// Could have already fielded the bye - in which case a
// transaction for the BYE cannot be created.
ex.printStackTrace();
assertTrue(new Exception().getStackTrace()[0].toString(), true);
} catch(Exception ex) {
ex.printStackTrace();
System.out.println("Failed to create a transaction for an incoming bye request.");
}
**/
byeTran = (ServerTransaction) byeEvt.getServerTransaction();
//We will now send an OK response and see that the
// Dialog enters a COMPLETED/TERMINATED state
//start listening for the response
try {
eventCollector.collectResponseEvent(riSipProvider);
} catch (TooManyListenersException e) {
throw new TckInternalError(
"Failed to register a SipListener with the RI.",
e);
}
try {
ok =
tiMessageFactory.createResponse(
Response.OK,
byeEvt.getRequest());
ok.addHeader(createTiContact());
byeTran.sendResponse(ok);
} catch (Exception e) {
throw new TiUnexpectedError(
"Failed to create and send an OK response",
e);
}
waitForMessage();
ResponseEvent byeOkRespEvt =
eventCollector.extractCollectedResponseEvent();
if (byeOkRespEvt == null || byeOkRespEvt.getResponse() == null)
throw new TiUnexpectedError("The TI did not send an OK response to a bye request.");
//The dialog should now be in the COMPLETED/TERMINATED state.
assertTrue(
"The dialog did not pass into a final (COMPLETED or TERMINATED) state after recieving a BYE.",
DialogState.COMPLETED.equals(dialog.getState())
|| DialogState.TERMINATED.equals(dialog.getState()));
} 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(DialogStateMachineTest.class);
}
}