You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by hu...@apache.org on 2009/10/01 19:19:21 UTC
svn commit: r820722 [7/8] - in /incubator/aries/contrib: ./ ibm/
ibm/aries.image/ ibm/build/ ibm/build/buildtasks/ ibm/build/buildtasks/src/
ibm/build/buildtasks/src/com/ ibm/build/buildtasks/src/com/ibm/
ibm/build/buildtasks/src/com/ibm/aries/ ibm/bui...
Added: incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCall.java
URL: http://svn.apache.org/viewvc/incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCall.java?rev=820722&view=auto
==============================================================================
--- incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCall.java (added)
+++ incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCall.java Thu Oct 1 17:19:13 2009
@@ -0,0 +1,558 @@
+/*
+ * (C) Copyright IBM Corp. 2005, 2009
+ */
+package com.ibm.aries.unittest.mocks;
+
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * <p>This class represents a method call that has been or is expected to be
+ * made. It encapsulates the class that the call was made on, the method
+ * that was invoked and the arguments passed.</p>
+ */
+public final class MethodCall
+{
+ /** An empty object array */
+ private static Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+ /** The name of the class invoked */
+ private String _className;
+ /** The array of interfaces implemented by the class */
+ private Class<?>[] _interfaces = new Class[0];
+ /** The method invoked */
+ private String _methodName;
+ /** The arguments passed */
+ private Object[] _arguments = EMPTY_OBJECT_ARRAY;
+ /** The object invoked */
+ private Object _invokedObject;
+ /** A list of comparators to use, instead of the objects .equals methods */
+ private static Map<Class<?>, Comparator<?>> equalsHelpers = new HashMap<Class<?>, Comparator<?>>();
+
+ /* ------------------------------------------------------------------------ */
+ /* MethodCall method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This constructor allows a MethodCall to be created when the class can be
+ * located statically, rather than dynamically.
+ *
+ * @param clazz The class.
+ * @param methodName The method name.
+ * @param arguments The arguments.
+ */
+ public MethodCall(Class<?> clazz, String methodName, Object ... arguments)
+ {
+ _className = clazz.getName();
+ _methodName = methodName;
+ _arguments = arguments;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* MethodCall method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method is used by the Skeleton in order create an instance of a
+ * MethodCall representing an invoked interface.
+ *
+ * NOTE: If possible changing this so the constructor does not need to be
+ * default visibility would be good, given the problems with default
+ * visibility.
+ *
+ * @param invokedObject The object that was invoked.
+ * @param methodName The name of the method invoked.
+ * @param arguments The arguments passed.
+ */
+ MethodCall(Object invokedObject, String methodName, Object ... arguments)
+ {
+ _className = invokedObject.getClass().getName();
+ _interfaces = invokedObject.getClass().getInterfaces();
+ _methodName = methodName;
+
+ this._arguments = (arguments == null) ? EMPTY_OBJECT_ARRAY : arguments;
+
+ _invokedObject = invokedObject;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getArguments method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns the arguments.
+ *
+ * @return The arguments.
+ */
+ public Object[] getArguments()
+ {
+ return _arguments;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getClassName method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Returns the name of the class the method was invoked or was defined on.
+ *
+ * @return the classname.
+ */
+ public String getClassName()
+ {
+ return _className;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getMethodName method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Returns the name of the method that was (or will be) invoked.
+ *
+ * @return the method name
+ */
+ public String getMethodName()
+ {
+ return _methodName;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* checkClassName method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method checks that the class names specified in the method call are
+ * compatible, i.e. one is a superclass of the other.
+ *
+ * @param one The first method call.
+ * @param two The second method call.
+ * @return true if the classes can be assigned to each other.
+ */
+ private boolean checkClassName(MethodCall one, MethodCall two)
+ {
+ // TODO make this stuff work better.
+ if (one._className.equals("java.lang.Object"))
+ {
+ return true;
+ }
+ else if (two._className.equals("java.lang.Object"))
+ {
+ return true;
+ }
+ else if (one._className.equals(two._className))
+ {
+ return true;
+ }
+ else
+ {
+ // check the other class name is one of the implemented interfaces
+ boolean result = false;
+
+ for (int i = 0; i < two._interfaces.length; i++)
+ {
+ if (two._interfaces[i].getName().equals(one._className))
+ {
+ result = true;
+ break;
+ }
+ }
+
+ if (!result)
+ {
+ for (int i = 0; i < one._interfaces.length; i++)
+ {
+ if (one._interfaces[i].getName().equals(two._className))
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* equals method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Returns true if and only if the two object represent the same call.
+ *
+ * @param obj The object to be compared.
+ * @return true if the specified object is the same as this.
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+
+ if (obj == null) return false;
+
+ if (obj == this) return true;
+
+ if (obj instanceof MethodCall)
+ {
+ MethodCall other = (MethodCall)obj;
+
+ if (!checkClassName(this, other))
+ {
+ return false;
+ }
+
+ if (!other._methodName.equals(this._methodName)) return false;
+ if (other._arguments.length != this._arguments.length) return false;
+
+ for (int i = 0; i < this._arguments.length; i++)
+ {
+ boolean thisArgNull = this._arguments[i] == null;
+ boolean otherArgClazz = other._arguments[i] instanceof Class;
+ boolean otherArgNull = other._arguments[i] == null;
+ boolean thisArgClazz = this._arguments[i] instanceof Class;
+
+ if (thisArgNull)
+ {
+ if (otherArgNull)
+ {
+ // This is OK
+ }
+ else if (otherArgClazz)
+ {
+ // This is also OK
+ }
+ else
+ {
+ return false;
+ }
+ // this argument is OK.
+ }
+ else if (otherArgNull)
+ {
+ if (thisArgClazz)
+ {
+ // This is OK
+ }
+ else
+ {
+ return false;
+ }
+ // this argument is OK.
+ }
+ else if (otherArgClazz)
+ {
+ if (thisArgClazz)
+ {
+ Class<?> otherArgClass = (Class<?>) other._arguments[i];
+ Class<?> thisArgClass = (Class<?>) this._arguments[i];
+
+ if (otherArgClass.equals(Class.class) || thisArgClass.equals(Class.class))
+ {
+ // do nothing
+ } else if (!(otherArgClass.isAssignableFrom(thisArgClass) ||
+ thisArgClass.isAssignableFrom(otherArgClass)))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ Class<?> clazz = (Class<?>)other._arguments[i];
+ if (clazz.isPrimitive())
+ {
+ if (clazz.equals(byte.class))
+ {
+ return this._arguments[i].getClass().equals(Byte.class);
+ }
+ else if (clazz.equals(boolean.class))
+ {
+ return this._arguments[i].getClass().equals(Boolean.class);
+ }
+ else if (clazz.equals(short.class))
+ {
+ return this._arguments[i].getClass().equals(Short.class);
+ }
+ else if (clazz.equals(char.class))
+ {
+ return this._arguments[i].getClass().equals(Character.class);
+ }
+ else if (clazz.equals(int.class))
+ {
+ return this._arguments[i].getClass().equals(Integer.class);
+ }
+ else if (clazz.equals(long.class))
+ {
+ return this._arguments[i].getClass().equals(Long.class);
+ }
+ else if (clazz.equals(float.class))
+ {
+ return this._arguments[i].getClass().equals(Float.class);
+ }
+ else if (clazz.equals(double.class))
+ {
+ return this._arguments[i].getClass().equals(Double.class);
+ }
+ }
+ else
+ {
+ if (!clazz.isInstance(this._arguments[i]))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else if (thisArgClazz)
+ {
+ Class<?> clazz = (Class<?>)this._arguments[i];
+ if (clazz.isPrimitive())
+ {
+ if (clazz.equals(byte.class))
+ {
+ return other._arguments[i].getClass().equals(Byte.class);
+ }
+ else if (clazz.equals(boolean.class))
+ {
+ return other._arguments[i].getClass().equals(Boolean.class);
+ }
+ else if (clazz.equals(short.class))
+ {
+ return other._arguments[i].getClass().equals(Short.class);
+ }
+ else if (clazz.equals(char.class))
+ {
+ return other._arguments[i].getClass().equals(Character.class);
+ }
+ else if (clazz.equals(int.class))
+ {
+ return other._arguments[i].getClass().equals(Integer.class);
+ }
+ else if (clazz.equals(long.class))
+ {
+ return other._arguments[i].getClass().equals(Long.class);
+ }
+ else if (clazz.equals(float.class))
+ {
+ return other._arguments[i].getClass().equals(Float.class);
+ }
+ else if (clazz.equals(double.class))
+ {
+ return other._arguments[i].getClass().equals(Double.class);
+ }
+ }
+ else
+ {
+ if (!clazz.isInstance(other._arguments[i]))
+ {
+ return false;
+ }
+ }
+ }
+ else if (this._arguments[i] instanceof Object[] && other._arguments[i] instanceof Object[])
+ {
+ return equals((Object[])this._arguments[i], (Object[])other._arguments[i]);
+ }
+ else
+ {
+ int result = compareUsingComparators(this._arguments[i], other._arguments[i]);
+
+ if (result == 0) continue;
+ else if (result == 1) return false;
+ else if (!!!this._arguments[i].equals(other._arguments[i])) return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Compare two arrays calling out to the custom comparators and handling
+ * AtomicIntegers nicely.
+ *
+ * TODO remove the special casing for AtomicInteger.
+ *
+ * @param arr1
+ * @param arr2
+ * @return true if the arrays are equals, false otherwise.
+ */
+ private boolean equals(Object[] arr1, Object[] arr2)
+ {
+ if (arr1.length != arr2.length) return false;
+
+ for (int k = 0; k < arr1.length; k++) {
+ if (arr1[k] == arr2[k]) continue;
+ if (arr1[k] == null && arr2[k] != null) return false;
+ if (arr1[k] != null && arr2[k] == null) return false;
+
+ int result = compareUsingComparators(arr1[k], arr2[k]);
+
+ if (result == 0) continue;
+ else if (result == 1) return false;
+
+ if (arr1[k] instanceof AtomicInteger && arr2[k] instanceof AtomicInteger &&
+ ((AtomicInteger)arr1[k]).intValue() == ((AtomicInteger)arr2[k]).intValue())
+ continue;
+
+ if (!!!arr1[k].equals(arr2[k])) return false;
+
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempt to do the comparison using the comparators. This logic returns:
+ *
+ * <ul>
+ * <li>0 if they are equal</li>
+ * <li>1 if they are not equal</li>
+ * <li>-1 no comparison was run</li>
+ * </ul>
+ *
+ * @param o1 The first object.
+ * @param o2 The second object.
+ * @return 0, 1 or -1 depending on whether the objects were equal, not equal or no comparason was run.
+ */
+ private int compareUsingComparators(Object o1, Object o2)
+ {
+ if (o1.getClass() == o2.getClass()) {
+ @SuppressWarnings("unchecked")
+ Comparator<Object> compare = (Comparator<Object>) equalsHelpers.get(o1.getClass());
+
+ if (compare != null) {
+ if (compare.compare(o1, o2) == 0) return 0;
+ else return 1;
+ }
+ }
+
+ return -1;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* hashCode method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Returns the hashCode (obtained by returning the hashCode of the
+ * methodName).
+ *
+ * @return The hashCode
+ */
+ @Override
+ public int hashCode()
+ {
+ return _methodName.hashCode();
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* toString method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Returns a string representation of the method call.
+ *
+ * @return string representation.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(this._className);
+ buffer.append('.');
+ buffer.append(this._methodName);
+ buffer.append("(");
+
+ for (int i = 0; i < this._arguments.length; i++)
+ {
+ if (this._arguments[i] != null)
+ {
+ if (this._arguments[i] instanceof Class)
+ {
+ buffer.append(((Class<?>)this._arguments[i]).getName());
+ }
+ else if (Proxy.isProxyClass(this._arguments[i].getClass()))
+ {
+ // If the object is a dynamic proxy, just use the proxy class name to avoid calling toString on the proxy
+ buffer.append(this._arguments[i].getClass().getName());
+ }
+ else if (this._arguments[i] instanceof Object[])
+ {
+ buffer.append(Arrays.toString((Object[])this._arguments[i]));
+ }
+ else
+ {
+ buffer.append(String.valueOf(this._arguments[i]));
+ }
+ }
+ else
+ {
+ buffer.append("null");
+ }
+
+ if (i + 1 < this._arguments.length)
+ buffer.append(", ");
+ }
+
+ buffer.append(")");
+ String string = buffer.toString();
+ return string;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getInterfaces method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns the list of interfaces implemented by the class that
+ * was called.
+ *
+ * @return Returns the interfaces.
+ */
+ public Class<?>[] getInterfaces()
+ {
+ return this._interfaces;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getInvokedObject method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns the invoked object.
+ *
+ * @return The object that was invoked or null if an expected call.
+ */
+ public Object getInvokedObject()
+ {
+ return _invokedObject;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* registerEqualsHelper method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * The native equals for an object may not provide the behaviour required by
+ * the tests. As an example AtomicInteger does not define a .equals, but tests
+ * may wish to compare it being passed in a method call for equality. This
+ * method allows a Comparator to be specified for any type and the Comparator
+ * will be used to determine equality in place of the .equals method.
+ *
+ * <p>The Comparator must not throw exceptions, and must return 0 for equality
+ * or any other integer for inequality.
+ * </p>
+ *
+ * @param <T> the type of the class and comparator.
+ * @param type the type of the class for which the comparator will be called.
+ * @param comparator the comparator to call.
+ */
+ public static <T> void registerEqualsHelper(Class<T> type, Comparator<T> comparator)
+ {
+ equalsHelpers.put(type, comparator);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* removeEqualsHelper method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method removes any registered comparator specified for the given type.
+ *
+ * @param type the type to remove the comparator from.
+ */
+ public static void removeEqualsHelper(Class<?> type)
+ {
+ equalsHelpers.remove(type);
+ }
+}
\ No newline at end of file
Added: incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCallHandler.java
URL: http://svn.apache.org/viewvc/incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCallHandler.java?rev=820722&view=auto
==============================================================================
--- incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCallHandler.java (added)
+++ incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/MethodCallHandler.java Thu Oct 1 17:19:13 2009
@@ -0,0 +1,20 @@
+/*
+ * (C) Copyright IBM Corp. 2009
+ */
+package com.ibm.aries.unittest.mocks;
+
+/**
+ * Implementations of this interface perform function when a method is called. The
+ * handler is provided details of the method called along with the skeleton used
+ * for the call.
+ */
+public interface MethodCallHandler
+{
+ /**
+ * @param methodCall the method that was called
+ * @param parent the skeleton it was called on
+ * @return an object to be returned (optional)
+ * @throws Exception an exception in case of failure.
+ */
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception;
+}
Added: incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/ReturnTypeHandler.java
URL: http://svn.apache.org/viewvc/incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/ReturnTypeHandler.java?rev=820722&view=auto
==============================================================================
--- incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/ReturnTypeHandler.java (added)
+++ incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/ReturnTypeHandler.java Thu Oct 1 17:19:13 2009
@@ -0,0 +1,23 @@
+/*
+ * (C) Copyright IBM Corp. 2009
+ */
+package com.ibm.aries.unittest.mocks;
+
+/**
+ * <p>Return type handlers return objects that implement the specified class.</p>
+ */
+public interface ReturnTypeHandler
+{
+
+ /**
+ * This method is called when a method call handler has not been registered
+ * and an object of a specific type needs to be returned. The handle method
+ * is called along with the type that is required.
+ *
+ * @param clazz the class to create an object for
+ * @param parent the skeleton requesting the class.
+ * @return an instance of the class, or something that can be assigned to it.
+ * @throws Exception if a failure occurs.
+ */
+ public Object handle(Class<?> clazz, Skeleton parent) throws Exception;
+}
\ No newline at end of file
Added: incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/Skeleton.java
URL: http://svn.apache.org/viewvc/incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/Skeleton.java?rev=820722&view=auto
==============================================================================
--- incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/Skeleton.java (added)
+++ incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/Skeleton.java Thu Oct 1 17:19:13 2009
@@ -0,0 +1,1344 @@
+/*
+ * (C) Copyright IBM Corp. 2005, 2009
+ */
+package com.ibm.aries.unittest.mocks;
+
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+
+import com.ibm.aries.unittest.mocks.annotations.InjectSkeleton;
+import com.ibm.aries.unittest.mocks.annotations.Singleton;
+
+/**
+ * <p>The Skeleton class is an implementation of the
+ * <code>java.lang.reflect.InvocationHandler</code> that can be used for
+ * dynamic mock objects.
+ * </p>
+ *
+ * <ol>
+ * <li>The static newMock methods can be used to create completely new mock
+ * objects backed by an entirely new skeleton.
+ * </li>
+ * <li>The static getSkeleton method can be used to obtain the skeleton
+ * backing a given mock.
+ * </li>
+ * <li>The createMock methods can be used to create a new mock object based on
+ * the skeleton that is invoked.
+ * </li>
+ * <li>The registerMethodCallHandler method can be used to register a handler
+ * that will be invoked when a method is called.
+ * </li>
+ * <li>The registerReturnTypeHandler method can be used to register a handler
+ * that will be invoked when a method with a specific return type is
+ * invoked. It should be noted that registered ReturnTypeHandlers will be
+ * invoked only if a method call handler has not been registered for the
+ * method that was invoked.
+ * </li>
+ * <li>The setReturnValue method can be used to set a value that will be
+ * returned when a method is invoked.
+ * </li>
+ * <li>The checkCalls methods can be used to determine if the methods in the
+ * list should have been called. They return a boolean to indicate if the
+ * expected calls occurred.
+ * </li>
+ * <li>The assertCalls method performs the same operation as the checkCalls,
+ * but throws an junit.framework.AssertionFailedError if the calls don't
+ * match. This intended for use within the junit test framework
+ * </li>
+ * <li>If no method call or return type handlers have been registered for a
+ * call then if the return type is an interface then a mock that implements
+ * that interface will be returned, otherwise null will be returned.
+ * </li>
+ * </ol>
+ *
+ * <p>SIB build component: sib.unittest.mediation</p>
+ *
+ * @author nottinga
+ * @version 1.20
+ * @since 1.0
+ */
+public final class Skeleton implements InvocationHandler
+{
+ /** A list of calls made on this skeleton */
+ private List<MethodCall> _methodCalls;
+ /** The invocation handler to call after MethodCall and ReturnType handlers */
+ private DefaultInvocationHandler default_Handler;
+ /** The method call handlers */
+ private Map<MethodCall, MethodCallHandler> _callHandlers;
+ /** The type handlers */
+ private Map<Class<?>, ReturnTypeHandler> _typeHandlers;
+ /** The parameter map */
+ private Map<String, Object> _mockParameters;
+ /** A Map of mock objects to Maps of properties */
+ private Map<Object, Map<String, Object>> _objectProperties;
+ /** A Map of exception notification listeners */
+ private Map<Class<?>, List<ExceptionListener>> _notificationListeners;
+ /** The template class used to create this Skeleton, may be null */
+ private Object _template;
+ /** Cached template objects */
+ private static ConcurrentMap<Object, SoftReference<Object>> _singletonMocks = new ConcurrentHashMap<Object, SoftReference<Object>>();
+
+ // Constructors
+
+ /* ------------------------------------------------------------------------ */
+ /* Skeleton constructor
+ /* ------------------------------------------------------------------------ */
+ /**
+ * constructs the skeleton with the default method call handlers and the
+ * default return type handlers.
+ */
+ private Skeleton()
+ {
+ reset();
+ }
+
+ // Static methods create methods
+
+ /* ------------------------------------------------------------------------ */
+ /* newMock method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns a completely new mock object backed by a new skeleton
+ * object. It is equivalent to
+ * <code>new Skeleton().createMock(interfaceClazzes)</code>
+ *
+ * @param interfaceClazzes the classes the mock should implement
+ * @return the new mock object.
+ */
+ public final static Object newMock(Class<?> ... interfaceClazzes)
+ {
+ return new Skeleton().createMock(interfaceClazzes);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* newMock method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns a completely new mock object backed by a new skeleton
+ * object. It is equivalent to
+ * <code>new Skeleton().createMock(interfaceClazzes)</code>
+ *
+ * @param <T> The object type.
+ * @param interfaceClazz the classes the mock should implement
+ * @return the new mock object.
+ */
+ public final static <T> T newMock(Class<T> interfaceClazz)
+ {
+ return interfaceClazz.cast(new Skeleton().createMock(interfaceClazz));
+ }
+
+ /**
+ * It is often the case that only a subset of methods on an interface are needed, but
+ * those methods that are needed are quite complex. In this case a static mock forces
+ * you into implementing lots of methods you do not need, and produces problems when
+ * new methods are added to the interface being implemented. This method can essentially
+ * be used to complete the interface implementation. The object passed in is an instance
+ * of a class that implements a subset of the methods on the supplied interface. It does
+ * not need to implement the interface itself. The returned object will implement the full
+ * interface and delegate to the methods on the templateObject where necessary.
+ *
+ * @param <T> The object type.
+ * @param template The template object for the mock
+ * @param interfaceClazz The interface to implement
+ * @return An implementation of the interface that delegates (where appropraite) onto the template.
+ */
+ public final static <T> T newMock(final Object template, Class<T> interfaceClazz)
+ {
+ Class<?> templateClass = template.getClass();
+
+ if (templateClass.getAnnotation(Singleton.class) != null) {
+ SoftReference<Object> mock = _singletonMocks.get(template);
+ if (mock != null) {
+ Object theMock = mock.get();
+ if (theMock == null) {
+ _singletonMocks.remove(template);
+ } else if (interfaceClazz.isInstance(theMock)) {
+ return interfaceClazz.cast(theMock);
+ }
+ }
+ }
+
+ Skeleton s = new Skeleton();
+ s._template = template;
+
+ for (Method m : interfaceClazz.getMethods()) {
+ try {
+ final Method m2 = templateClass.getMethod(m.getName(), m.getParameterTypes());
+
+ MethodCall mc = new MethodCall(interfaceClazz, m.getName(), (Object[])m.getParameterTypes());
+ s.registerMethodCallHandler(mc, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+
+ try {
+ m2.setAccessible(true);
+ return m2.invoke(template, methodCall.getArguments());
+ } catch (InvocationTargetException ite) {
+ if(ite.getTargetException() instanceof Exception)
+ throw (Exception)ite.getTargetException();
+ else throw new Exception(ite.getTargetException());
+ }
+ }
+ });
+ } catch (NoSuchMethodException e) {
+ // do nothing here, it is a method not on the interface so ignore it.
+ }
+ }
+
+ Field[] fs = template.getClass().getFields();
+
+ for (Field f : fs) {
+ InjectSkeleton sk = f.getAnnotation(InjectSkeleton.class);
+
+ if (sk != null) {
+ f.setAccessible(true);
+ try {
+ f.set(template, s);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ Object o = s.createMock(interfaceClazz);
+ _singletonMocks.put(template, new SoftReference<Object>(o) {
+ @Override
+ public boolean enqueue()
+ {
+ _singletonMocks.remove(template);
+
+ System.out.println("Done cleanup");
+
+ return super.enqueue();
+ }
+ });
+ return interfaceClazz.cast(o);
+ }
+
+ // static mock query methods
+
+ /* ------------------------------------------------------------------------ */
+ /* getSkeleton method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns the Skeleton backing the supplied mock object. If the
+ * supplied object is not a mock an IllegalArgumentException will be thrown.
+ *
+ * @param mock the mock object
+ * @return the skeleton backing the mock object
+ * @throws IllegalArgumentException thrown if the object is not a mock.
+ */
+ public final static Skeleton getSkeleton(Object mock)
+ throws IllegalArgumentException
+ {
+ InvocationHandler ih = Proxy.getInvocationHandler(mock);
+ if (ih instanceof Skeleton)
+ {
+ return (Skeleton)ih;
+ }
+ throw new IllegalArgumentException("The supplied proxy (" + mock + ") was not a (Jetstream mediations) dynamic mock ");
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* isSkeleton method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns true if and only the provided object is backed by a
+ * Skeleton. Another way to think about this is if it returns true then a
+ * call to getSkeleton will not result in an IllegalArgumentException, and is
+ * guaranteed to return a Skeleton.
+ *
+ * @param mock the mock to test.
+ * @return true if it is backed by a skeleton.
+ */
+ public final static boolean isSkeleton(Object mock)
+ {
+ if (Proxy.isProxyClass(mock.getClass())) {
+ InvocationHandler ih = Proxy.getInvocationHandler(mock);
+
+ return (ih instanceof Skeleton);
+ }
+
+ return false;
+ }
+
+ // InvocationHandler defined methods.
+
+ /* ------------------------------------------------------------------------ */
+ /* invoke method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method is invoked by the mock objects. It constructs a MethodCall
+ * object representing the call and adds it to the list of calls that were
+ * made. (It should be noted that if the method is toString, hashCode or
+ * equals then they are not added to the list.) It then calls a registered
+ * MethodCallHandler, if a MethodCallHandler is not registered then a
+ * ReturnTypeHandler is invoked. If a ReturnTypeHandler is not invoked then
+ * the registered default InvocationHandler is called. By default the Skeleton
+ * is constructed with a DefaultInvocationHandler. If the invoked method has
+ * an interface as a return type then the DefaultInvocationHandler will return
+ * a new mock implementing that interface. If the return type is a class null
+ * will be returned.
+ *
+ * @param targetObject The mock object that was invoked.
+ * @param calledMethod The method that was called.
+ * @param arguments The arguments that were passed.
+ * @return The return of the method invoked.
+ * @throws Throwable Any exception thrown.
+ */
+ public Object invoke(Object targetObject, Method calledMethod, Object[] arguments)
+ throws Throwable
+ {
+ String methodName = calledMethod.getName();
+ MethodCall call = new MethodCall(targetObject, methodName, arguments);
+
+ if (!DefaultMethodCallHandlers.isDefaultMethodCall(call))
+ {
+ _methodCalls.add(call);
+ }
+
+ Object result;
+
+ try
+ {
+ if (_callHandlers.containsKey(call))
+ {
+ MethodCallHandler handler = _callHandlers.get(call);
+ result = handler.handle(call, this);
+ }
+ else if (isReadWriteProperty(targetObject.getClass(), calledMethod))
+ {
+ String propertyName = methodName.substring(3);
+ if (methodName.startsWith("get") || methodName.startsWith("is"))
+ {
+ if (methodName.startsWith("is")) propertyName = methodName.substring(2);
+
+ Map<String, Object> properties = _objectProperties.get(targetObject);
+ if (properties == null)
+ {
+ properties = new HashMap<String, Object>();
+ _objectProperties.put(targetObject, properties);
+ }
+
+ if (properties.containsKey(propertyName))
+ {
+ result = properties.get(propertyName);
+ }
+ else if (_typeHandlers.containsKey(calledMethod.getReturnType()))
+ {
+ result = createReturnTypeProxy(calledMethod.getReturnType());
+ }
+ else
+ {
+ result = default_Handler.invoke(targetObject, calledMethod, arguments);
+ }
+ }
+ // Must be a setter.
+ else
+ {
+ Map<String, Object> properties = _objectProperties.get(targetObject);
+ if (properties == null)
+ {
+ properties = new HashMap<String, Object>();
+ _objectProperties.put(targetObject, properties);
+ }
+
+ properties.put(propertyName, arguments[0]);
+ result = null;
+ }
+ }
+ else if (_typeHandlers.containsKey(calledMethod.getReturnType()))
+ {
+ result = createReturnTypeProxy(calledMethod.getReturnType());
+ }
+ else
+ {
+ result = default_Handler.invoke(targetObject, calledMethod, arguments);
+ }
+ }
+ catch (Throwable t)
+ {
+ Class<?> throwableType = t.getClass();
+ List<ExceptionListener> listeners = _notificationListeners.get(throwableType);
+ if (listeners != null)
+ {
+ for (ExceptionListener listener : listeners)
+ {
+ listener.exceptionNotification(t);
+ }
+ }
+
+ throw t;
+ }
+
+ return result;
+ }
+
+ // MethodCall registration methods
+
+ /* ------------------------------------------------------------------------ */
+ /* registerMethodCallHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method registers a MethodCallHandler for the specified MethodCall.
+ *
+ * @param call The method that was called.
+ * @param handler The MethodCallHandler.
+ */
+ public void registerMethodCallHandler(MethodCall call, MethodCallHandler handler)
+ {
+ deRegisterMethodCallHandler(call);
+ _callHandlers.put(call, handler);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* deRegisterMethodCallHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method removes a registered MethodCallHandler for the specified
+ * MethodCall.
+ *
+ * @param call the specified MethodCall
+ */
+ public void deRegisterMethodCallHandler(MethodCall call)
+ {
+ _callHandlers.remove(call);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* reset method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method resets the skeleton to the state it was in prior just after it
+ * was constructed.
+ */
+ public void reset()
+ {
+ _methodCalls = new LinkedList<MethodCall>();
+ _callHandlers = new HashMap<MethodCall, MethodCallHandler>();
+ _typeHandlers = new HashMap<Class<?>, ReturnTypeHandler>();
+ DefaultReturnTypeHandlers.registerDefaultHandlers(this);
+ DefaultMethodCallHandlers.registerDefaultHandlers(this);
+ default_Handler = new DefaultInvocationHandler(this);
+ _mockParameters = new HashMap<String, Object>();
+ _objectProperties = new HashMap<Object, Map<String, Object>>();
+ _notificationListeners = new HashMap<Class<?>, List<ExceptionListener>>();
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* clearMethodCalls method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method clears the method calls list for the skeleton
+ */
+ public void clearMethodCalls()
+ {
+ _methodCalls = new LinkedList<MethodCall>();
+ }
+
+
+ /* ------------------------------------------------------------------------ */
+ /* setReturnValue method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This is a convenience method for registering a method call handler where
+ * a specific value should be returned when a method is called, rather than
+ * some logic needs to be applied. The value should be an object or the object
+ * version of the primitive for the methods return type, so if the method
+ * returns short the value must be an instance of java.lang.Short, not
+ * java.lang.Integer.
+ *
+ * @param call the method being called.
+ * @param value the value to be returned when that method is called.
+ */
+ public void setReturnValue(MethodCall call, final Object value)
+ {
+ Class<?> clazz;
+ try {
+ clazz = Class.forName(call.getClassName());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("This should be impossible as we have already seen this class loaded");
+ }
+
+
+ Method[] methods = clazz.getMethods();
+
+ methods: for (Method m : methods) {
+ if(!!!m.getName().equals(call.getMethodName()))
+ continue methods;
+
+ Object[] args = call.getArguments();
+ Class<?>[] parms = m.getParameterTypes();
+
+ if (args.length == parms.length) {
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] instanceof Class && args[i].equals(parms[i])) {
+ } else if (parms[i].isInstance(args[i])) {
+ } else {
+ continue methods;
+ }
+ }
+
+ Class<?> returnType = m.getReturnType();
+ if (returnType.isPrimitive()) {
+ if (returnType == boolean.class) returnType = Boolean.class;
+ else if (returnType == byte.class) returnType = Byte.class;
+ else if (returnType == short.class) returnType = Short.class;
+ else if (returnType == char.class) returnType = Character.class;
+ else if (returnType == int.class) returnType = Integer.class;
+ else if (returnType == long.class) returnType = Long.class;
+ else if (returnType == float.class) returnType = Float.class;
+ else if (returnType == double.class) returnType = Double.class;
+ }
+
+ if (value != null && !!!returnType.isInstance(value)) {
+ throw new IllegalArgumentException("The object cannot be returned by the requested method: " + call);
+ } else break methods;
+ }
+ }
+
+
+
+ registerMethodCallHandler(call, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+ return value;
+ }
+ });
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* setThrow method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This is a convenience method for registering a method call handler where
+ * a specific exception should be thrown when the method is called, rather
+ * than some logic needs to be applied.
+ *
+ * @param call the method being called
+ * @param thingToThrow the exception to throw.
+ */
+ public void setThrows(MethodCall call, final Exception thingToThrow)
+ {
+ registerMethodCallHandler(call, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+ thingToThrow.fillInStackTrace();
+ throw thingToThrow;
+ }
+ });
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* setThrow method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This is a convenience method for registering a method call handler where
+ * a specific exception should be thrown when the method is called, rather
+ * than some logic needs to be applied.
+ *
+ * @param call the method being called
+ * @param thingToThrow the exception to throw.
+ */
+ public void setThrows(MethodCall call, final Error thingToThrow)
+ {
+ registerMethodCallHandler(call, new MethodCallHandler()
+ {
+ public Object handle(MethodCall methodCall, Skeleton parent) throws Exception
+ {
+ thingToThrow.fillInStackTrace();
+ throw thingToThrow;
+ }
+ });
+ }
+
+ // ReturnType registration methods
+
+ /* ------------------------------------------------------------------------ */
+ /* registerReturnTypeHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method registers a ReturnTypeHandler for the specified class.
+ *
+ * @param clazz The class to be handled.
+ * @param handler The ReturnTypeHandler
+ */
+ public void registerReturnTypeHandler(Class<?> clazz, ReturnTypeHandler handler)
+ {
+ deRegisterReturnTypeHandler(clazz);
+ _typeHandlers.put(clazz, handler);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* deRegisterReturnTypeHandler method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method removes a registration for a ReturnTypeHandler for the
+ * specified class.
+ *
+ * @param clazz The class to deregister the handler for.
+ */
+ public void deRegisterReturnTypeHandler(Class<?> clazz)
+ {
+ _typeHandlers.remove(clazz);
+ }
+
+ // Exception notification methods
+
+ /* ------------------------------------------------------------------------ */
+ /* registerExceptionListener method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method registers an ExceptionListener when the specified Exception is
+ * thrown.
+ *
+ * @param throwableType The type of the Throwable
+ * @param listener The listener.
+ */
+ public void registerExceptionListener(Class<?> throwableType, ExceptionListener listener)
+ {
+ List<ExceptionListener> l = _notificationListeners.get(throwableType);
+ if (l == null)
+ {
+ l = new ArrayList<ExceptionListener>();
+ _notificationListeners.put(throwableType, l);
+ }
+ l.add(listener);
+ }
+
+ // parameter related methods
+
+ /* ------------------------------------------------------------------------ */
+ /* setParameter method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method allows a parameter to be set. It is intended to be used by
+ * MethodCallHandlers and ReturnTypeHandlers.
+ *
+ * @param key The key
+ * @param value The value
+ */
+ public void setParameter(String key, Object value)
+ {
+ _mockParameters.put(key, value);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getParameter method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method allows a parameter to be retrieved.
+ *
+ * @param key the key the parameter was set using
+ * @return the parameter
+ */
+ public Object getParameter(String key)
+ {
+ return _mockParameters.get(key);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getTemplateObject method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * @return the template object, if one was used when initializing this skeleton.
+ */
+ public Object getTemplateObject()
+ {
+ return _template;
+ }
+
+ // default InvocationHandler related methods
+
+ /**
+ * @param defaultHandler The defaultHandler to set.
+ */
+ public void setDefaultHandler(DefaultInvocationHandler defaultHandler)
+ {
+ this.default_Handler = defaultHandler;
+ }
+
+ // MethodCall list check methods
+
+ /* ------------------------------------------------------------------------ */
+ /* checkCalls method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method checks that the calls in the list occurred. If the addCalls
+ * boolean is true then their must be an exact match. If the allCalls boolean
+ * is false then the calls in the list must occur in that order, but other
+ * calls can be in between.
+ *
+ * @param calls The expected calls list
+ * @param allCalls true if an exact match comparison should be performed
+ * @return true if they the expected calls match.
+ */
+ public boolean checkCalls(List<MethodCall> calls, boolean allCalls)
+ {
+ boolean found = false;
+ if (allCalls)
+ {
+ return calls.equals(_methodCalls);
+ }
+ else
+ {
+ Iterator<MethodCall> actual = _methodCalls.iterator();
+ for (MethodCall expectedCall : calls)
+ {
+ found = false;
+ actual: while (actual.hasNext())
+ {
+ MethodCall actualCall = actual.next();
+ if (actualCall.equals(expectedCall))
+ {
+ found = true;
+ break actual;
+ }
+ }
+ }
+ }
+
+ return found;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* checkCall method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Checks that the specified method has been called on this skeleton
+ *
+ * @param call the call that should have been called.
+ * @return true if the MethodCall occurs in the list.
+ */
+ public boolean checkCall(MethodCall call)
+ {
+ return this._methodCalls.contains(call);
+ }
+
+ // MethodCall list assert methods
+
+ /* ------------------------------------------------------------------------ */
+ /* assertCalls method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method checks that the MethodCalls objects in the given list were
+ * made and throws an AssertionFailedError if they were not. If allCalls is
+ * true the given list and the calls list must be identical. If allCalls is
+ * false other calls could have been made on the skeleton in between ones
+ * specified in the list.
+ *
+ * @param calls the list of calls
+ * @param allCalls whether an exact match between the lists is required
+ * @throws AssertionFailedError if a failure has occurred.
+ */
+ public void assertCalled(List<MethodCall> calls, boolean allCalls) throws AssertionFailedError
+ {
+ if (allCalls)
+ {
+ if ((calls == null) && (_methodCalls == null)) return;
+
+ if (calls == null)
+ {
+ throw new AssertionFailedError("expected null, but was " + _methodCalls);
+ }
+
+ if (_methodCalls == null)
+ {
+ throw new AssertionFailedError("expected:" + calls + " but was null");
+ }
+
+ if (calls.equals(_methodCalls)) return;
+
+ // OK compare lists and decide on differences - initially all the lists are different
+ int startOfDifferences = 0;
+ // Remove the common start of sequence
+ boolean lastItemSame = true;
+
+ for (int i = 0; i < calls.size() && i < _methodCalls.size() && lastItemSame; i++)
+ {
+ if ((calls.get(i) == null) && (_methodCalls.get(i) == null))
+ {
+ lastItemSame = true;
+ }
+ else if ((calls.get(i) == null) || (_methodCalls.get(i) == null))
+ {
+ lastItemSame = false;
+ }
+ else
+ {
+ lastItemSame = calls.get(i).equals(_methodCalls.get(i));
+ }
+
+ if (lastItemSame) startOfDifferences++;
+
+ }//for
+ // Now remove the common bit at the end
+ int endOfDifferencesInExpected = calls.size();
+ int endOfDifferencesInReceived = _methodCalls.size();
+ lastItemSame = true;
+
+ while ((endOfDifferencesInExpected > startOfDifferences)
+ && (endOfDifferencesInReceived > startOfDifferences)
+ && lastItemSame)
+ {
+ int ap = endOfDifferencesInExpected - 1;
+ int bp = endOfDifferencesInReceived - 1;
+
+ if ((calls.get(ap) == null) && (_methodCalls.get(bp) == null))
+ {
+ lastItemSame = true;
+ }
+ else if ((calls.get(ap) == null) || (_methodCalls.get(bp) == null))
+ {
+ lastItemSame = false;
+ }
+ else
+ {
+ lastItemSame = calls.get(ap).equals(_methodCalls.get(bp));
+ }
+
+ if (lastItemSame)
+ {
+ endOfDifferencesInExpected--;
+ endOfDifferencesInReceived--;
+ }
+
+ }//while
+
+ String failureText;
+ // OK, now build the failureText
+ if (endOfDifferencesInExpected == startOfDifferences)
+ {
+ failureText =
+ "Expected calls and actual calls differed because "
+ + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived)
+ + " inserted after element "
+ + startOfDifferences;
+
+ }
+ else if (endOfDifferencesInReceived == startOfDifferences)
+ {
+ failureText =
+ "Expected calls and actual calls differed because "
+ + calls.subList(startOfDifferences, endOfDifferencesInExpected)
+ + " missing after element "
+ + startOfDifferences;
+
+ }
+ else
+ {
+ if ((endOfDifferencesInExpected == startOfDifferences + 1)
+ && (endOfDifferencesInReceived == startOfDifferences + 1))
+ {
+
+ failureText =
+ "Expected calls and actual calls differed because element "
+ + startOfDifferences
+ + " is different (calls:"
+ + calls.get(startOfDifferences)
+ + " but was:"+_methodCalls.get(startOfDifferences)+") ";
+
+ }
+ else if (endOfDifferencesInExpected == startOfDifferences + 1)
+ {
+
+ failureText =
+ "Expected calls and actual calls differed because element "
+ + startOfDifferences
+ + " ("
+ + calls.get(startOfDifferences)
+ + ") has been replaced by "
+ + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived);
+ }
+ else
+ {
+ failureText =
+ "Expected calls and actual calls differed because elements between "
+ + startOfDifferences
+ + " and "
+ + (endOfDifferencesInExpected - 1)
+ + " are different (expected:"
+ + calls.subList(startOfDifferences, endOfDifferencesInExpected)
+ + " but was:"
+ + _methodCalls.subList(startOfDifferences, endOfDifferencesInReceived)
+ + ")";
+ }//if
+ }//if
+
+ throw new AssertionFailedError(failureText + " expected:" + calls + " but was:" + _methodCalls);
+ }
+ else
+ {
+ Iterator<MethodCall> expected = calls.iterator();
+ Iterator<MethodCall> actual = _methodCalls.iterator();
+ while (expected.hasNext())
+ {
+ boolean found = false;
+ MethodCall expectedCall = expected.next();
+ MethodCall actualCall = null;
+
+ actual: while (actual.hasNext())
+ {
+ actualCall = actual.next();
+ if (actualCall.equals(expectedCall))
+ {
+ found = true;
+ break actual;
+ }
+ }
+
+ if (!found)
+ {
+ throw new AssertionFailedError( "The method call " +
+ expectedCall +
+ " was expected but has not occurred (actual calls = "+_methodCalls+")");
+ }
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* assertCall method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This does the same as checkCall, but throws an
+ * junit.framework.AssertionFailedError if the call did not occur.
+ *
+ * @param call the call that was expected
+ */
+ public void assertCalled(MethodCall call)
+ {
+ if (!checkCall(call))
+ {
+ throw new AssertionFailedError("The method call " +
+ call +
+ " was expected but has not occurred. Actual calls: " + getMethodCallsAsString());
+ }
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* assertCalledExactNumberOfTimes method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method asserts that the method specified in the call parameter has
+ * been called the number of times specified by numberOfCalls. If
+ * numberOfCalls is zero this method is equivalent to assertNotCalled.
+ *
+ * @param call The call that was made.
+ * @param numberOfCalls The number of times the call should have been made.
+ */
+ public void assertCalledExactNumberOfTimes(MethodCall call, int numberOfCalls)
+ {
+ int callCount = 0;
+
+ for (MethodCall callMade : _methodCalls)
+ {
+ if (callMade.equals(call))
+ {
+ callCount++;
+ }
+ }
+
+ if (numberOfCalls != callCount)
+ {
+ throw new AssertionFailedError("The method call " + call +
+ " should have been called " + numberOfCalls +
+ " time(s), but was called " + callCount + " time(s)");
+ }
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* assertNotCalled method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method throws an junit.framework.AssertionFailedError if the specified
+ * call was invoked on the skeleton.
+ *
+ * @param call the call to check.
+ */
+ public void assertNotCalled(MethodCall call)
+ {
+ Assert.assertFalse( "The method call " +
+ call +
+ "occurred in the skeleton " +
+ this.toString(), checkCall(call));
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* assertMockNotCalled method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method throws an junit.framework.AssertionFailedError if the skeleton
+ * has had any methods invoked on it.
+ */
+ public void assertSkeletonNotCalled()
+ {
+ Assert.assertEquals("The skeleton " + this.toString() +
+ " has had the following method invoked on it " + getMethodCallsAsString(),
+ 0, _methodCalls.size());
+ }
+
+ // Instance mock creation methods
+
+ /* ------------------------------------------------------------------------ */
+ /* createMock method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Creates a new Mock using this skeleton backing it.
+ *
+ * @param interfaceClasses an array of interface the mock should implement.
+ * @return the mock
+ */
+ public Object createMock(Class<?> ... interfaceClasses)
+ {
+ ClassLoader cl;
+
+ if (interfaceClasses.length > 0) cl = interfaceClasses[0].getClassLoader();
+ else cl = Thread.currentThread().getContextClassLoader();
+
+ return Proxy.newProxyInstance(cl, interfaceClasses, this);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* createMock method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * Creates a new Mock using this skeleton backing it.
+ *
+ * @param <T> The object type
+ * @param interfaceClass an array of interface the mock should implement.
+ * @return the mock
+ */
+ public <T> T createMock(Class<T> interfaceClass)
+ {
+ return interfaceClass.cast(Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] {interfaceClass}, this));
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* invokeReturnTypeHandlers method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method invokes the return type proxy for the specified class. If a
+ * ReturnTypeHandler for that type has not been registered then if the class
+ * represents an interface a new mock will be returned, backed by this
+ * skeleton, otherwise null will be returned.
+ *
+ * @param type the type to be invoked.
+ * @return the returned object.
+ * @throws Exception if an error occurs when invoking the return type handler.
+ */
+ public Object invokeReturnTypeHandlers(Class<?> type) throws Exception
+ {
+ if (_typeHandlers.containsKey(type))
+ {
+ ReturnTypeHandler rth = _typeHandlers.get(type);
+ return rth.handle(type, this);
+ }
+ else if (type.isInterface())
+ {
+ return createMock(type);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ // Miscellaneous methods that have been deprecated and will be removed.
+
+ /* ------------------------------------------------------------------------ */
+ /* createReturnTypeProxy method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * create a proxy for given return type.
+ *
+ * @deprecated use invokeReturnTypeHandlers instead
+ *
+ * @param type The return type for which a handler is required
+ * @return ReturnTypeHandler The return type handler
+ * @throws Exception Thrown if an exception occurs.
+ */
+ /* ------------------------------------------------------------------------ */
+ @Deprecated
+ private final Object createReturnTypeProxy(Class<?> type) throws Exception
+ {
+ return invokeReturnTypeHandlers(type);
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* isReadWriteProperty method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns true if the method passed a getter for a read write
+ * java bean property. This is worked out by checking that a setter and getter
+ * exist for the property and that the setter and getter take and return
+ * exactly the same time.
+ *
+ * @param objClass The object the read write method has been invoked on.
+ * @param method The method to be checked.
+ * @return true if it is a getter or setter for a read write property.
+ */
+ private boolean isReadWriteProperty(Class<?> objClass, Method method)
+ {
+ String methodName = method.getName();
+ String propertyName = methodName.substring(3);
+ Class<?>[] parameters = method.getParameterTypes();
+ Class<?> clazz;
+ boolean result = false;
+
+ if (methodName.startsWith("get") && parameters.length == 0)
+ {
+ clazz = method.getReturnType();
+ try
+ {
+ objClass.getMethod("set" + propertyName, clazz);
+ result = true;
+ }
+ catch (NoSuchMethodException e)
+ {
+ if (isPrimitive(clazz))
+ {
+ clazz = getOtherForm(clazz);
+ try
+ {
+ objClass.getMethod("set" + propertyName, clazz);
+ result = true;
+ }
+ catch (NoSuchMethodException e1)
+ {
+
+ }
+ }
+ }
+ }
+ else if (methodName.startsWith("is") && parameters.length == 0)
+ {
+ clazz = method.getReturnType();
+ if (clazz.equals(Boolean.class) || clazz.equals(boolean.class))
+ {
+ propertyName = methodName.substring(2);
+ try
+ {
+ objClass.getMethod("set" + propertyName, clazz);
+ result = true;
+ }
+ catch (NoSuchMethodException e)
+ {
+
+ if (isPrimitive(clazz))
+ {
+ clazz = getOtherForm(clazz);
+ try
+ {
+ objClass.getMethod("set" + propertyName, clazz);
+ result = true;
+ }
+ catch (NoSuchMethodException e1)
+ {
+
+ }
+ }
+ }
+ }
+ }
+ else if (methodName.startsWith("set") && parameters.length == 1)
+ {
+ clazz = parameters[0];
+
+ try
+ {
+ Method getter = objClass.getMethod("get" + propertyName, new Class[0]);
+ result = checkClasses(getter.getReturnType(), clazz);
+ }
+ catch (NoSuchMethodException e)
+ {
+ if (isPrimitive(clazz))
+ {
+ clazz = getOtherForm(clazz);
+ try
+ {
+ Method getter = objClass.getMethod("get" + propertyName, new Class[0]);
+ result = checkClasses(getter.getReturnType(), clazz);
+ }
+ catch (NoSuchMethodException e1)
+ {
+ if (clazz.equals(Boolean.class) || clazz.equals(boolean.class))
+ {
+ try
+ {
+ Method getter = objClass.getMethod("is" + propertyName, new Class[0]);
+ result = checkClasses(getter.getReturnType(), clazz);
+ }
+ catch (NoSuchMethodException e2)
+ {
+ clazz = getOtherForm(clazz);
+ try
+ {
+ Method getter = objClass.getMethod("is" + propertyName, new Class[0]);
+ result = checkClasses(getter.getReturnType(), clazz);
+ }
+ catch (NoSuchMethodException e3)
+ {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ /* ------------------------------------------------------------------------ */
+ /* isPrimitive method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns true if the class object represents a primitive or the
+ * object version of a primitive.
+ *
+ * @param clazz The class to be checked.
+ * @return true if it is a primitive or a wrapper.
+ */
+ private boolean isPrimitive(Class<?> clazz)
+ {
+ boolean result = false;
+
+ if (clazz.isPrimitive())
+ {
+ result = true;
+ }
+ else
+ {
+ result = clazz.equals(Boolean.class) || clazz.equals(Byte.class) ||
+ clazz.equals(Short.class) || clazz.equals(Character.class) ||
+ clazz.equals(Integer.class) || clazz.equals(Long.class) ||
+ clazz.equals(Float.class) || clazz.equals(Double.class);
+ }
+
+ return result;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getOtherForm method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method takes a class representing either a primitive or an object
+ * wrapper. If the class is a primitive type the object wrapper class is
+ * returned. If the class is an object wrapper class the primitive type is
+ * returned.
+ *
+ * @param clazz
+ * @return the class representing the primitive object wrapper.
+ */
+ private Class<?> getOtherForm(Class<?> clazz)
+ {
+ Class<?> result = null;
+
+ if (clazz.equals(boolean.class)) result = Boolean.class;
+ else if (clazz.equals(byte.class)) result = Byte.class;
+ else if (clazz.equals(short.class)) result = Short.class;
+ else if (clazz.equals(char.class)) result = Character.class;
+ else if (clazz.equals(int.class)) result = Integer.class;
+ else if (clazz.equals(long.class)) result = Long.class;
+ else if (clazz.equals(float.class)) result = Float.class;
+ else if (clazz.equals(double.class)) result = Double.class;
+ else if (clazz.equals(Boolean.class)) result = boolean.class;
+ else if (clazz.equals(Byte.class)) result = byte.class;
+ else if (clazz.equals(Short.class)) result = short.class;
+ else if (clazz.equals(Character.class)) result = char.class;
+ else if (clazz.equals(Integer.class)) result = int.class;
+ else if (clazz.equals(Long.class)) result = long.class;
+ else if (clazz.equals(Float.class)) result = float.class;
+ else if (clazz.equals(Double.class)) result = double.class;
+
+ return result;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* checkClasses method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method returns true if the two classes are the same or if one is
+ * primitive that the other is a primitive wrapper.
+ *
+ * @param type1
+ * @param type2
+ * @return true if the classes are compatible.
+ */
+ private boolean checkClasses(Class<?> type1, Class<?> type2)
+ {
+ boolean result = false;
+
+ if ((type1.isPrimitive() && type2.isPrimitive()) ||
+ (!type1.isPrimitive() && !type2.isPrimitive()))
+ {
+ result = type1.equals(type2);
+ }
+ else
+ {
+ result = (type1.equals(boolean.class) && type2.equals(Boolean.class)) ||
+ (type1.equals(byte.class) && type2.equals(Byte.class)) ||
+ (type1.equals(short.class) && type2.equals(Short.class)) ||
+ (type1.equals(char.class) && type2.equals(Character.class)) ||
+ (type1.equals(int.class) && type2.equals(Integer.class)) ||
+ (type1.equals(long.class) && type2.equals(Long.class)) ||
+ (type1.equals(float.class) && type2.equals(Float.class)) ||
+ (type1.equals(double.class) && type2.equals(Double.class)) ||
+ (type2.equals(boolean.class) && type1.equals(Boolean.class)) ||
+ (type2.equals(byte.class) && type1.equals(Byte.class)) ||
+ (type2.equals(short.class) && type1.equals(Short.class)) ||
+ (type2.equals(char.class) && type1.equals(Character.class)) ||
+ (type2.equals(int.class) && type1.equals(Integer.class)) ||
+ (type2.equals(long.class) && type1.equals(Long.class)) ||
+ (type2.equals(float.class) && type1.equals(Float.class)) ||
+ (type2.equals(double.class) && type1.equals(Double.class));
+ }
+
+ return result;
+ }
+
+ /* ------------------------------------------------------------------------ */
+ /* getMethodCallsAsString method
+ /* ------------------------------------------------------------------------ */
+ /**
+ * This method builds a String that contains the method calls that have been
+ * made on this skeleton. It puts each call on a separate line.
+ *
+ * @return the string representation of the method call.
+ */
+ private String getMethodCallsAsString()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ for (MethodCall call : _methodCalls)
+ {
+ builder.append(call);
+ builder.append("\r\n");
+ }
+
+ return builder.toString();
+ }
+
+}
\ No newline at end of file
Added: incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/InjectSkeleton.java
URL: http://svn.apache.org/viewvc/incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/InjectSkeleton.java?rev=820722&view=auto
==============================================================================
--- incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/InjectSkeleton.java (added)
+++ incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/InjectSkeleton.java Thu Oct 1 17:19:13 2009
@@ -0,0 +1,20 @@
+/*
+ * (C) Copyright IBM Corp. 2009
+ */
+package com.ibm.aries.unittest.mocks.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * If a field on a template is marked with this annotation then it will be
+ * injected with the Skeleton instance for the most recently created mock.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface InjectSkeleton
+{
+
+}
\ No newline at end of file
Added: incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/Singleton.java
URL: http://svn.apache.org/viewvc/incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/Singleton.java?rev=820722&view=auto
==============================================================================
--- incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/Singleton.java (added)
+++ incubator/aries/contrib/ibm/unittest.framework/src/com/ibm/aries/unittest/mocks/annotations/Singleton.java Thu Oct 1 17:19:13 2009
@@ -0,0 +1,22 @@
+/*
+ * (C) Copyright IBM Corp. 2009
+ */
+package com.ibm.aries.unittest.mocks.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be applied to template objects. If a template object's
+ * class has this annotation and is passed multiple times to the Skeleton.newMock
+ * method with the same interface class the same mock will be returned, unless
+ * the garbage collector has cleared the previous mock.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Singleton
+{
+
+}
\ No newline at end of file