Clover coverage report - gsbase - 2.0.1
Coverage timestamp: Sat Jan 1 2005 12:30:02 EST
file stats: LOC: 251   Methods: 12
NCLOC: 119   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
EqualsTester.java 79.4% 88.5% 100% 86.7%
coverage coverage
 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.Constructor;
 42    import java.lang.reflect.Method;
 43    import java.lang.reflect.Modifier;
 44    import junit.framework.Assert;
 45   
 46    /**
 47    * EqualsTester is used to test the equals contract on objects. The contract as
 48    * specified by java.lang.Object states that if A.equals(B) is true then B.equals(A)
 49    * is also true. It also specifies that if A.equals(B) is true then A.hashCode()
 50    * will equals B.hashCode().<p>
 51    *
 52    * It is also common practice to implement equals using an instanceof check which
 53    * will result in false positives in some cases. Specifically, it will result in
 54    * false positives when comparing against a subclass with the same values. For an
 55    * in-depth discussion of the common problems when implementing the equals contract,
 56    * refer to the book "Practical Java" by Peter Haggar
 57    * <pre>
 58    * // WRONG way of implementing equals
 59    * public boolean equals( final Object object ) {
 60    * if( object instanceof this ) {
 61    * // do check
 62    * }
 63    * return false;
 64    * }
 65    * </pre>
 66    *
 67    * The correct way to implement equals is as follows
 68    * <pre>
 69    * public boolean equals( final Object object ) {
 70    * if( object != null && object.getClass() == this.getClass() ) {
 71    * // do check
 72    * }
 73    * return false;
 74    * }
 75    * </pre>
 76    *
 77    * EqualsTester ensures that the equals() and hashCode() methods have been
 78    * implemented correctly.<p>
 79    *
 80    * <pre>
 81    * final Object a = new Foo(4); // original object
 82    * final Object b = new Foo(4); // another object that has the same values as the original
 83    * final Object c = new Foo(5); // another object with different values
 84    * final Object d = new Foo(4) {}; // a subclass of Foo with the same values as the original
 85    * new EqualsTester(a, b, c, d);
 86    * </pre>
 87    *
 88    * @version $Revision: 1.5 $
 89    * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
 90    */
 91    public class EqualsTester extends Assert {
 92   
 93    /**
 94    * Perform the test. The act of instantiating one of these will run the test.
 95    *
 96    * @param a The object to be tested
 97    * @param b An object that is equal to A
 98    * @param c An object of the same class that is not equal to A. If it is
 99    * not possible to create a different one then pass null.
 100    * @param d A subclass of A with the same values. If A is an instance of a
 101    * final class then this must be null
 102    */
 103  9 public EqualsTester( final Object a, final Object b, final Object c, final Object d ) {
 104  9 assertNotNull( a, "A" );
 105  8 assertNotNull( b, "B" );
 106  7 assertSameClassAsA( a, b, "B" );
 107   
 108  7 if( c == null ) {
 109  2 assertCAllowedToBeNull( a.getClass() );
 110    }
 111    else {
 112  5 assertSameClassAsA( a, c, "C" );
 113    }
 114   
 115  6 if( isClassFinal( a.getClass() ) ) {
 116  2 assertNull( d, "D" );
 117    }
 118  4 else if( d == null ) {
 119  1 throw new DetailedNullPointerException( "D", "Cannot be null for a non-final class" );
 120    }
 121   
 122  5 if( d != null ) {
 123  3 assertDDifferentClassThanA( a, d );
 124    }
 125   
 126  5 assertAEqualsNull( a );
 127  4 assertAEqualsA( a );
 128  4 assertAEqualsB( a, b );
 129  4 if( c != null ) {
 130  3 assertANotEqualC( a, c );
 131    }
 132  4 assertClassAndSubclass( a, d );
 133    }
 134   
 135   
 136  6 private boolean isClassFinal( final Class clazz ) {
 137  6 final int modifiers = clazz.getModifiers();
 138  6 return Modifier.isFinal( modifiers );
 139    }
 140   
 141   
 142  4 private void assertAEqualsA( final Object a ) {
 143  4 assertTrue( "A.equals(A)", a.equals( a ) );
 144    }
 145   
 146   
 147  4 private void assertAEqualsB( final Object a, final Object b ) {
 148  4 assertTrue( "A.equals(B)", a.equals( b ) );
 149  4 assertTrue( "B.equals(A)", b.equals( a ) );
 150  4 assertEquals( "hashCode", a.hashCode(), b.hashCode() );
 151    }
 152   
 153   
 154  3 private void assertANotEqualC( final Object a, final Object c ) {
 155  3 assertTrue( "a.equals(c)", a.equals( c ) == false );
 156  3 assertTrue( "c.equals(a)", c.equals( a ) == false );
 157    }
 158   
 159   
 160  4 private void assertClassAndSubclass( final Object a, final Object d ) {
 161  4 if( d != null ) {
 162  3 if( a.equals( d ) == true ) {
 163  1 fail( "a.equals(d)" );
 164    }
 165   
 166  2 if( d.equals( a ) == true ) {
 167  0 fail( "d.equals(a)" );
 168    }
 169    }
 170    }
 171   
 172   
 173   
 174  17 private void assertNotNull( final Object object, final String description ) {
 175  17 if( object == null ) {
 176  2 fail( description + " is null" );
 177    }
 178    }
 179   
 180   
 181  2 private void assertNull( final Object object, final String description ) {
 182  2 if( object != null ) {
 183  0 fail( description + " must be null. Found [" + object + "]" );
 184    }
 185    }
 186   
 187   
 188    /**
 189    * C may not be null if it has a public non-default constructor or any
 190    * setXX() methods
 191    *
 192    * @param clazz
 193    */
 194  2 private void assertCAllowedToBeNull( final Class clazz ) {
 195  2 int i;
 196   
 197  2 final Constructor constructors[] = clazz.getConstructors();
 198  2 for( i = 0; i < constructors.length; i++ ) {
 199  1 if( constructors[i].getParameterTypes().length != 0 ) {
 200  1 fail( "C may not be null because it has a public non-default constructor: " + constructors[i] );
 201    }
 202    }
 203   
 204  1 final Method methods[] = clazz.getMethods();
 205  1 for( i = 0; i < methods.length; i++ ) {
 206  9 if( methods[i].getName().startsWith( "set" ) ) {
 207  0 fail( "C may not be null because it has public set methods: " + methods[i] );
 208    }
 209    }
 210    }
 211   
 212   
 213  12 private void assertSameClassAsA( final Object a, final Object object, final String name ) {
 214  12 if( a.getClass() != object.getClass() ) {
 215  0 fail(
 216    name
 217    + " must be of same class as A. A.class=["
 218    + a.getClass().getName()
 219    + "] "
 220    + name
 221    + ".class=["
 222    + object.getClass().getName()
 223    + "]" );
 224    }
 225    }
 226   
 227   
 228  3 private void assertDDifferentClassThanA( final Object a, final Object d ) {
 229  3 if( a.getClass() == d.getClass() ) {
 230  0 fail(
 231    "D must not be of same class as A. A.class=["
 232    + a.getClass().getName()
 233    + "] d.class=["
 234    + d.getClass().getName()
 235    + "]" );
 236    }
 237    }
 238   
 239   
 240  5 private void assertAEqualsNull( final Object a ) {
 241  5 try {
 242  4 if( a.equals( null ) == true ) {
 243  0 fail( "A.equals(null) returned true" );
 244    }
 245    }
 246    catch( final NullPointerException e ) {
 247  1 fail( "a.equals(null) threw a NullPointerException. It should have returned false" );
 248    }
 249    }
 250    }
 251