package org.jacorb.test.harness;
import junit.framework.AssertionFailedError;
import org.junit.experimental.categories.Category;
import org.omg.Messaging.ExceptionHolder;
/**
* A special <code>ClientServerTestCase</code> for testing asynchronous
* method invocations following the callback model.
* <p>
* This class makes it convenient to issue an asynchronous invocation
* and wait for the reply within the same test case. There is an abstract
* <code>ReplyHandler</code> defined as an inner class of this class.
* It provides a synchronization mechanism so that the main thread of
* control that executes the test case can wait on the reply handler
* until it has received and analyzed the reply.
* <p>
* As an example, assume that you want to test asynchronous invocations
* of an interface <code>MyServer</code>. You would create a subclass
* of <code>CallbackTestCase</code>, and extend the generic
* <code>ReplyHandler</code> for <code>MyServer</code> as follows:
*
* <p><blockquote><pre>
* public class MyServerTest extends CallbackTestCase
* {
* private ReplyHandler extends CallbackTestCase.ReplyHandler
* implements MyServerOperations
* {
* // ...
* }
*
* }
* </pre></blockquote><p>
*
* Each method from <code>MyServerOperations</code> should be
* implemented to call <code>wrong_reply()</code> (for reply methods) or
* <code>wrong_exception()</code> (for <code>_excep</code> methods). In
* the actual test cases, these methods will in turn be overwritten
* anonymously for the correct replies.
* <p>
* Your type-specific <code>ReplyHandler</code> will be turned into a
* CORBA object using the tie approach. This is best done in a
* convenience method such as the following:
*
* <p><blockquote><pre>
* private AMI_MyServerHandler ref ( ReplyHandler handler )
* {
* AMI_MyServerHandlerPOATie tie =
* new AMI_MyServerHandlerPOATie( handler )
* {
* public org.omg.CORBA.portable.OutputStream
* _invoke( String method,
* org.omg.CORBA.portable.InputStream _input,
* org.omg.CORBA.portable.ResponseHandler handler )
* throws org.omg.CORBA.SystemException
* {
* try
* {
* return super._invoke( method, _input, handler );
* }
* catch( AssertionFailedError e )
* {
* return null;
* }
* }
* };
* return tie._this( setup.getClientOrb() );
* }
* </pre></blockquote><p>
*
* The <code>_invoke()</code> method of the POATie is overridden anonymously
* here to catch JUnit's <code>AssertionFailedError</code>s. Without this,
* a failed test case would cause all subsequent test cases to fail as well.
* You can copy this method verbatim into your own source, except for
* changing the name of the AMI classes.
* <p>
* An individual test case may then look like this:
*
* <p><blockquote><pre>
* public void test_some_operation()
* {
* ReplyHandler handler = new ReplyHandler()
* {
* public void some_operation( int ami_return_val )
* {
* assertEquals( 123, ami_return_val );
* pass();
* }
* };
* ( ( _CallbackServerStub ) server )
* .sendc_some_operation( ref( handler ) );
* handler.wait_for_reply( 200 );
* }
* </pre></blockquote><p>
*
* The <code>ReplyHandler</code> is extended anonymously to handle and
* analyze the expected reply. You may use any of the
* <code>assert...()</code> methods defined in
* <code>org.junit.Assert</code> to do this (they are redefined in
* the generic <code>ReplyHandler</code>). Unlike in a normal test case,
* you must also call the <code>pass()</code> method explicitly if the
* test case did not fail. After the <code>ReplyHandler</code> has been
* defined, the asynchronous operation is invoked using the corresponding
* <code>sendc_</code> method. The calling thread then waits for a reply
* for at most 200 milliseconds. If no reply is received within that
* time, the test case fails.
*
* @author Andre Spiegel <spiegel@gnu.org>
*/
@Category(IMRExcludedClientServerCategory.class)
public class CallbackTestCase extends ClientServerTestCase
{
protected abstract class ReplyHandler
{
private boolean replyReceived = false;
private boolean testFailed = false;
private String failureMessage = null;
protected ReplyHandler() {
super();
}
public synchronized void wait_for_reply(long timeout)
{
try
{
long start = System.currentTimeMillis();
while(!replyReceived && System.currentTimeMillis() < start + timeout)
{
this.wait(timeout);
}
if ( !replyReceived )
{
org.junit.Assert.fail
( "no reply within timeout ("
+ timeout + "ms)" );
}
if ( testFailed )
{
org.junit.Assert.fail( failureMessage );
}
}
catch ( InterruptedException e )
{
org.junit.Assert.fail
( "interrupted while waiting for reply" );
}
finally
{
replyReceived = false;
testFailed = false;
failureMessage = null;
}
}
public synchronized void fail(String message)
{
replyReceived = true;
testFailed = true;
failureMessage = message;
this.notifyAll();
throw new AssertionFailedError();
}
public void wrong_reply( String name )
{
fail( "wrong reply method: " + name );
}
public void wrong_exception( String methodName,
ExceptionHolder excep_holder )
{
try
{
excep_holder.raise_exception();
}
catch( Exception e )
{
fail( "unexpected exception: "
+ methodName + ", " + e );
}
}
public Exception getException( ExceptionHolder excep_holder )
{
try
{
excep_holder.raise_exception();
return null;
}
catch( Exception e )
{
return e;
}
}
public synchronized void pass()
{
replyReceived = true;
testFailed = false;
failureMessage = null;
this.notifyAll();
}
// The assert methods below are lifted from org.junit.Assert.
// The only change is to make them non-static here.
/**
* Asserts that two bytes are equal.
*/
public void assertEquals(byte expected, byte actual)
{
assertEquals(null, expected, actual);
}
/**
* Asserts that two chars are equal.
*/
public void assertEquals(char expected, char actual)
{
assertEquals(null, expected, actual);
}
/**
* Asserts that two doubles are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
public void assertEquals(
double expected,
double actual,
double delta)
{
assertEquals(null, expected, actual, delta);
}
/**
* Asserts that two floats are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
public void assertEquals(float expected, float actual, float delta)
{
assertEquals(null, expected, actual, delta);
}
/**
* Asserts that two ints are equal.
*/
public void assertEquals(int expected, int actual)
{
assertEquals(null, expected, actual);
}
/**
* Asserts that two longs are equal.
*/
public void assertEquals(long expected, long actual)
{
assertEquals(null, expected, actual);
}
/**
* Asserts that two objects are equal. If they are not
* an AssertionFailedError is thrown.
*/
public void assertEquals(Object expected, Object actual)
{
assertEquals(null, expected, actual);
}
/**
* Asserts that two bytes are equal.
*/
public void assertEquals(String message, byte expected, byte actual)
{
assertEquals(message, new Byte(expected), new Byte(actual));
}
/**
* Asserts that two chars are equal.
*/
public void assertEquals(String message, char expected, char actual)
{
assertEquals(
message,
new Character(expected),
new Character(actual));
}
/**
* Asserts that two doubles are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
public void assertEquals(
String message,
double expected,
double actual,
double delta)
{
// handle infinity specially since subtracting to infinite values gives NaN and the
// the following test fails
if (Double.isInfinite(expected))
{
if (!(expected == actual))
failNotEquals(
message,
new Double(expected),
new Double(actual));
}
else if (
!(Math.abs(expected - actual) <= delta))
// Because comparison with NaN always returns false
failNotEquals(
message,
new Double(expected),
new Double(actual));
}
/**
* Asserts that two floats are equal concerning a delta. If the expected
* value is infinity then the delta value is ignored.
*/
public void assertEquals(
String message,
float expected,
float actual,
float delta)
{
// handle infinity specially since subtracting to infinite values gives NaN and the
// the following test fails
if (Float.isInfinite(expected))
{
if (!(expected == actual))
failNotEquals(
message,
new Float(expected),
new Float(actual));
}
else if (!(Math.abs(expected - actual) <= delta))
failNotEquals(message, new Float(expected), new Float(actual));
}
/**
* Asserts that two ints are equal.
*/
public void assertEquals(String message, int expected, int actual)
{
assertEquals(message, new Integer(expected), new Integer(actual));
}
/**
* Asserts that two longs are equal.
*/
public void assertEquals(String message, long expected, long actual)
{
assertEquals(message, new Long(expected), new Long(actual));
}
/**
* Asserts that two objects are equal. If they are not
* an AssertionFailedError is thrown.
*/
public void assertEquals(
String message,
Object expected,
Object actual)
{
if (expected == null && actual == null)
return;
if (expected != null && expected.equals(actual))
return;
failNotEquals(message, expected, actual);
}
/**
* Asserts that two shorts are equal.
*/
public void assertEquals(String message, short expected, short actual)
{
assertEquals(message, new Short(expected), new Short(actual));
}
/**
* Asserts that two booleans are equal.
*/
public void assertEquals(
String message,
boolean expected,
boolean actual)
{
assertEquals(message, new Boolean(expected), new Boolean(actual));
}
/**
* Asserts that two shorts are equal.
*/
public void assertEquals(short expected, short actual)
{
assertEquals(null, expected, actual);
}
/**
* Asserts that two booleans are equal.
*/
public void assertEquals(boolean expected, boolean actual)
{
assertEquals(null, expected, actual);
}
/**
* Asserts that an object isn't null.
*/
public void assertNotNull(Object object)
{
assertNotNull(null, object);
}
/**
* Asserts that an object isn't null.
*/
public void assertNotNull(String message, Object object)
{
assertTrue(message, object != null);
}
/**
* Asserts that an object is null.
*/
public void assertNull(Object object)
{
assertNull(null, object);
}
/**
* Asserts that an object is null.
*/
public void assertNull(String message, Object object)
{
assertTrue(message, object == null);
}
/**
* Asserts that two objects refer to the same object. If they are not
* the same an AssertionFailedError is thrown.
*/
public void assertSame(Object expected, Object actual)
{
assertSame(null, expected, actual);
}
/**
* Asserts that two objects refer to the same object. If they are not
* an AssertionFailedError is thrown.
*/
public void assertSame(String message, Object expected, Object actual)
{
if (expected == actual)
return;
failNotSame(message, expected, actual);
}
/**
* Asserts that a condition is true. If it isn't it throws
* an AssertionFailedError with the given message.
*/
public void assertTrue(String message, boolean condition)
{
if (!condition)
fail(message);
}
/**
* Asserts that a condition is true. If it isn't it throws
* an AssertionFailedError.
*/
public void assertTrue(boolean condition)
{
assertTrue(null, condition);
}
private void failNotEquals( String message,
Object expected,
Object actual )
{
String formatted = "";
if (message != null)
formatted = message + " ";
fail( formatted + "expected:<" + expected
+ "> but was:<" + actual + ">");
}
private void failNotSame( String message,
Object expected,
Object actual )
{
String formatted = "";
if (message != null)
formatted = message + " ";
fail(formatted + "expected same");
}
}
}