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.util.Date;
42  import junit.framework.AssertionFailedError;
43  import junit.framework.TestCase;
44  
45  /***
46   *  Tests for EqualsTester
47   *
48   * @version  $Revision: 1.9 $
49   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
50   */
51  public class EqualsTesterTest extends TestCase {
52      /***
53       *  Create an instance
54       *
55       * @param  name Name of the test
56       */
57      public EqualsTesterTest( final String name ) {
58          super( name );
59      }
60  
61  
62      /***
63       *  Test the good case
64       */
65      public void testProperlyImplementedClass() {
66          final ProperlyImplementedEquals a = new ProperlyImplementedEquals( 200 );
67          final ProperlyImplementedEquals b = new ProperlyImplementedEquals( 200 );
68          final ProperlyImplementedEquals c = new ProperlyImplementedEquals( 400 );
69          final ProperlyImplementedEquals d =
70              new ProperlyImplementedEquals( 200 ) {
71              };
72  
73          new EqualsTester( a, b, c, d );
74      }
75  
76  
77      /***
78       *  Test various combinations of null parameters
79       */
80      public void testEquals_NullValues() {
81          final Date a = new Date( 200 );
82          final Date b = new Date( 200 );
83          final Date c = new Date( 400 );
84          final Date d = new java.sql.Date( 200 );
85  
86          try {
87              new EqualsTester( null, b, c, d );
88              fail( "Expected failure for null A" );
89          }
90          catch( final AssertionFailedError expected ) {
91              // Expected path
92          }
93  
94          try {
95              new EqualsTester( a, null, c, d );
96              fail( "Expected failure for null B" );
97          }
98          catch( final AssertionFailedError expected ) {
99              // Expected path
100         }
101 
102         try {
103             new EqualsTester( a, b, null, d );
104             fail( "Expected failure for null C" );
105         }
106         catch( final AssertionFailedError expected ) {
107             // Expected path
108         }
109     }
110 
111 
112     /***
113      *  Test passing in null when the class we are testing is final. This should
114      *  be legal
115      */
116     public void testNullDForFinalClass() {
117         final EqualChecker equalChecker =
118             new EqualChecker() {
119                 public boolean equals( final TestObject object1, final TestObject object2 ) {
120                     return object2 != null && object1.value_ == object2.value_;
121                 }
122             };
123         final TestObject a = new TestObject( equalChecker, 200 );
124         final TestObject b = new TestObject( equalChecker, 200 );
125         final TestObject c = new TestObject( equalChecker, 400 );
126         final TestObject d = null;
127 
128         try {
129             new EqualsTester( a, b, c, d );
130         }
131         catch( final NullPointerException e ) {
132             fail( "EqualsTester should allow null for D when class is final" );
133         }
134     }
135 
136 
137     /***
138      * Test passing in a null D for a non-final class
139      */
140     public void testNullDForNonFinalClass() {
141         /*** Fake class for testing */
142         class Foo {
143             private boolean state;
144 
145             /*** @param b The state */
146             public Foo( final boolean b ) {
147                 state = b;
148             }
149 
150             /***
151              * @param object The object to compare against
152              * @return true if the objects are equal 
153              */
154             public boolean equals( final Object object ) {
155                 if( object != null && object.getClass() == this.getClass() ) {
156                     return state == ( (Foo)object ).state;
157                 }
158                 return false;
159             }
160             
161             /*** @return the hash code */
162             public int hashCode() {
163                 return 2;
164             }
165         }
166 
167         final Foo a = new Foo( true );
168         final Foo b = new Foo( true );
169         final Foo c = new Foo( false );
170         final Foo d = null;
171 
172         try {
173             new EqualsTester( a, b, c, d );
174             fail( "EqualsTester should not allow null for D when class is not final" );
175         }
176         catch( final DetailedNullPointerException e ) {
177             assertEquals("D", e.getArgumentName());
178         }
179     }
180 
181 
182     /***
183      *  Test passing in a subclass that has a badly implemented equals method
184      */
185     public void testSubclassWithBadEquals() {
186         final Date a = new Date( 200 );
187         final Date b = new Date( 200 );
188         final Date c = new Date( 400 );
189         final Date d =
190             new Date( 200 ) {
191                 private static final long serialVersionUID = 1L;
192 				public boolean equals( final Object object ) {
193                     return true;
194                 }
195                 public int hashCode() {
196                     return 2;
197                 }
198             };
199 
200         try {
201             new EqualsTester( a, b, c, d );
202             fail( "Class and subclass cannot be equal" );
203         }
204         catch( final AssertionFailedError e ) {
205             assertEquals( "a.equals(d)", e.getMessage() );
206         }
207     }
208 
209 
210     /***
211      *  Test a.equals(a)
212      */
213     public void testEquals_AequalsA() {
214         final EqualChecker equalChecker =
215             new EqualChecker() {
216                 public boolean equals( final TestObject object1, final TestObject object2 ) {
217                     if( object1 == object2 ) {
218                         return false;
219                     }
220                     else {
221                         return object1.value_ == object2.value_;
222                     }
223                 }
224             };
225 
226         final TestObject a = new TestObject( equalChecker, 200 );
227         final TestObject b = new TestObject( equalChecker, 200 );
228         final TestObject c = new TestObject( equalChecker, 400 );
229         final TestObject d = null;
230         try {
231             new EqualsTester( a, b, c, d );
232             fail( "A.equals(A)" );
233         }
234         catch( final AssertionFailedError e ) {
235             // Expected path
236         }
237     }
238 
239 
240     private static final class TestObject {
241         private final EqualChecker equalChecker_;
242         private final int value_;
243 
244 
245         /***
246          *  Create an instance
247          *
248          * @param  value
249          * @param  equalChecker
250          */
251         public TestObject( final EqualChecker equalChecker, final int value ) {
252             equalChecker_ = equalChecker;
253             value_ = value;
254         }
255 
256         /*** @inheritDoc Object#equals(Object) */
257         public boolean equals( final Object object ) {
258             return equalChecker_.equals( this, (TestObject)object );
259         }
260 
261         /*** @inheritDoc Object#hashCode() */
262         public int hashCode() {
263             return value_;
264         }
265     }
266 
267 
268     private static class EqualChecker {
269     		/***
270     		 * Compare these two objects for equality
271     		 * @param object1 The first object
272     		 * @param object2 The second Object
273     		 * @return True if they are equal.
274     		 */
275         public boolean equals( final TestObject object1, final TestObject object2 ) {
276             return object1 == object2;
277         }
278     }
279 
280 
281     private class ProperlyImplementedEquals {
282         private final int value_;
283 
284         /*** @param value Some value */
285         public ProperlyImplementedEquals( final int value ) {
286             value_ = value;
287         }
288 
289         /*** @inheritDoc Object#equals(Object) */
290         public boolean equals( final Object object ) {
291             if( object != null && this.getClass() == object.getClass() ) {
292                 final ProperlyImplementedEquals pie = (ProperlyImplementedEquals)object;
293                 return value_ == pie.value_;
294             }
295             return false;
296         }
297 
298         /*** @inheritDoc Object#hashCode() */
299         public int hashCode() {
300             return value_;
301         }
302     }
303 }
304