View Javadoc

1   /*
2    * Copyright (c) 1998, 2005 Gargoyle Software Inc. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    *
7    * 1. Redistributions of source code must retain the above copyright notice,
8    *    this list of conditions and the following disclaimer.
9    * 2. Redistributions in binary form must reproduce the above copyright notice,
10   *    this list of conditions and the following disclaimer in the documentation
11   *    and/or other materials provided with the distribution.
12   * 3. The end-user documentation included with the redistribution, if any, must
13   *    include the following acknowledgment:
14   *
15   *       "This product includes software developed by Gargoyle Software Inc.
16   *        (http://www.GargoyleSoftware.com/)."
17   *
18   *    Alternately, this acknowledgment may appear in the software itself, if
19   *    and wherever such third-party acknowledgments normally appear.
20   * 4. The name "Gargoyle Software" must not be used to endorse or promote
21   *    products derived from this software without prior written permission.
22   *    For written permission, please contact info@GargoyleSoftware.com.
23   * 5. Products derived from this software may not be called "GSBase", nor may
24   *    "GSBase" appear in their name, without prior written permission of
25   *    Gargoyle Software Inc.
26   *
27   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
28   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
29   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
30   * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
33   * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37   */
38  package com.gargoylesoftware.base.testing;
39  
40  import com.gargoylesoftware.base.util.DetailedNullPointerException;
41  import java.lang.reflect.InvocationTargetException;
42  import java.lang.reflect.Method;
43  import java.io.ByteArrayInputStream;
44  import java.io.ByteArrayOutputStream;
45  import java.io.IOException;
46  import java.io.ObjectInputStream;
47  import java.io.ObjectOutputStream;
48  import junit.framework.AssertionFailedError;
49  
50  
51  /***
52   * Utility methods dealing with JUnit testing.
53   *
54   * @version $Revision: 1.7 $
55   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
56   */
57  public class TestUtil {
58  
59      // private constructor to prevent instantiation
60      private TestUtil() {
61      }
62  
63      /***
64       * Serialize the specified object to a stream and then deserialize
65       * it again.  This tests the following things:
66       * <ol>
67       * <li>Ensures that the original and reconstructed objects
68       *     are equal.
69       * <li>The object can be serialized
70       * </ol>
71       *
72       * @param object The object to test
73       * @param checkEquality True if the original and copy should be
74       *        equal according to the semantics of the equals() method.
75       * @return The copy.
76       * @throws IOException If an error occcurs during serialization.
77       */
78      public static Object testSerialization(
79              final Object object,
80              final boolean checkEquality )
81          throws
82              IOException {
83  
84          assertNotNull("object", object);
85  
86          final Object copy = copyBySerialization(object);
87  
88          if( checkEquality ) {
89              checkEquality( object, copy );
90          }
91  
92          return copy;
93      }
94  
95      /***
96       * Same as testSerialization(object, true).  Provided as a convenience as
97       * the equality check is usually wanted.
98       * @param object The object to test
99       * @return The copy.
100      * @throws IOException If an error occcurs during serialization.
101      */
102     public static Object testSerialization(
103             final Object object )
104         throws
105             IOException {
106 
107         return testSerialization( object, true );
108     }
109 
110     /***
111      * Copy an object by serializing it into a buffer and then deserializing
112      * it again.
113      *
114      * @param object The original.
115      * @return The copy.
116      * @throws IOException If an error occcurs during serialization.
117      */
118     public static Object copyBySerialization( final Object object )
119         throws
120             IOException {
121 
122         assertNotNull("object", object);
123 
124         try {
125             final ByteArrayOutputStream bos = new ByteArrayOutputStream();
126             final ObjectOutputStream oos = new ObjectOutputStream(bos);
127             oos.writeObject(object);
128             oos.flush();
129 
130             final byte[] b = bos.toByteArray();
131             final ByteArrayInputStream bis = new ByteArrayInputStream(b);
132             final ObjectInputStream ois = new ObjectInputStream(bis);
133             final Object copy = ois.readObject();
134 
135             return copy;
136         }
137         catch( final ClassNotFoundException e ) {
138             // Theoretically impossible
139             throw new NoClassDefFoundError("Class not found: "+e.getMessage());
140         }
141     }
142 
143     /***
144      * The the clone() method on an object.
145      * @param object The original object to clone.
146      * @param checkEquality True if the original and copy are to be compared
147      * for equality after the clone().
148      * @throws IllegalAccessException If we do not have authority to call the
149      * clone() method.
150      * @throws InvocationTargetException If an exception is thrown during
151      * the processing of the clone() method
152      */
153     public static void testClone( final Object object, final boolean checkEquality )
154         throws
155             IllegalAccessException,
156             InvocationTargetException {
157 
158         if( object instanceof Cloneable == false ) {
159             throw new AssertionFailedError("Object is not cloneable");
160         }
161 
162         Method cloneMethod;
163         try {
164             cloneMethod = object.getClass().getDeclaredMethod("clone", new Class[0] );
165         }
166         catch( final NoSuchMethodException e ) {
167             throw new AssertionFailedError("Object does not have a public clone() method");
168         }
169 
170         final Object copy = cloneMethod.invoke( object, new Object[0] );
171         if( checkEquality ) {
172             checkEquality( object, copy );
173         }
174     }
175 
176     /***
177      * Assert that the two objects are equal
178      * @param original The original object
179      * @param copy The object to compare against
180      */
181     private static void checkEquality( final Object original, final Object copy ) {
182         if( copy.equals( original ) == false ) {
183             throw new AssertionFailedError("Objects are different: original=["+original+"] copy=["+copy+"]");
184         }
185 
186         if( copy.hashCode() != original.hashCode() ) {
187             throw new AssertionFailedError("Hashcodes are different: original=["
188                                            +original.hashCode()+"] copy=["
189                                            +copy.hashCode()+"]");
190         }
191     }
192 
193 
194     /***
195      * Assert that a and b appear equal.  See {@link #appearsEqual(Object,Object)}
196      * for an explanation of "appears"
197      *
198      * @param message The message to display if the assert fails.
199      * @param a The first object to compare
200      * @param b The second object to compare
201      */
202     public static void assertAppearsEqual(
203         final String message, final Object a, final Object b ) {
204 
205         if( appearsEqual(a, b) == false ) {
206             throw new AssertionFailedError(message);
207         }
208     }
209 
210 
211     /***
212      * Assert that a and b do not appear equal.  See {@link #appearsEqual(Object,Object)}
213      * for an explanation of "appears"
214      *
215      * @param message The message to display if the assert fails.
216      * @param a The first object to compare
217      * @param b The second object to compare
218      */
219     public static void assertAppearsNotEqual(
220         final String message, final Object a, final Object b ) {
221 
222         if( appearsEqual(a, b) == true ) {
223             throw new AssertionFailedError(message);
224         }
225     }
226 
227 
228     /***
229      * Return true if the two objects appear to be equal.  Some objects cannot
230      * be compared for equality because they don't implement either the equals
231      * method or the Comparable interface.  An example would be the Event objects
232      * used by AWT and Swing.<p>
233      *
234      * This method will attempt to determine if the two objects are equal by
235      * calling all the public accessor methods on the objects and performing
236      * equals checks on the results.<p>
237      *
238      * If an exception is thrown during the invocation of any of the getXX()
239      * methods then that method will be ignored for the purpose of considering
240      * equality.
241      *
242      * @param a The first object to be compared
243      * @param b The second object to be compared
244      * @return True if the two objects appear to be the same.
245      */
246     public static boolean appearsEqual( final Object a, final Object b ) {
247         if( a == null && b == null ) {
248             return true;
249         }
250         if( a == null || b == null ) {
251             return false;
252         }
253         final Class clazz = a.getClass();
254         if( b.getClass() != clazz ) {
255             return false;
256         }
257 
258         final Object noArgs[] = new Object[0];
259         final Method getMethods[] = clazz.getDeclaredMethods();
260         for( int i=0; i<getMethods.length; i++ ) {
261             final Method method = getMethods[i];
262             if( method.getParameterTypes().length == 0 ) {
263                 try {
264                     if( isEqual( method.invoke(a, noArgs), method.invoke(b, noArgs) ) == false ) {
265                         return false;
266                     }
267                 }
268                 catch( final IllegalAccessException e ) {
269                     // Ignore this method
270                     e.printStackTrace();
271                 }
272                 catch( final InvocationTargetException e ) {
273                     // Ignore this method
274                     e.printStackTrace();
275                 }
276             }
277         }
278 
279         return true;
280     }
281 
282 
283     private static boolean isEqual( final Object a, final Object b ) {
284         final boolean isEqual;
285         if( a == null && b == null ) {
286             isEqual = true;
287         }
288         else if( a == null || b == null ) {
289             isEqual = false;
290         }
291         else {
292             isEqual = a.equals(b);
293         }
294 
295         return isEqual;
296     }
297 
298 
299     /***
300      * Verify that the specified value is not null.  If it is then throw an exception
301      *
302      * @param fieldName The name of the field to check
303      * @param fieldValue The value of the field to check
304      * @exception DetailedNullPointerException If fieldValue is null
305      */
306     public static final void assertNotNull( final String fieldName, final Object fieldValue )
307         throws DetailedNullPointerException {
308 
309         if( fieldValue == null ) {
310             throw new DetailedNullPointerException(fieldName);
311         }
312     }
313 }
314